I have the following model:
class Articles(models.Model):
status = models.CharField(choices=STATUSES, max_length=10, default='online')
categ = models.ForeignKey(ArticlesCategories)
title = models.CharField(max_length=64)
content = models.TextField()
def get_edit_url(self):
return '/articles/add/%d' % self.pk
edit_url = property(get_edit_url)
def __unicode__(self):
return self.title
class Meta:
db_table = 'articles'
and the url route:
url(r'^add/(?P<article_id>\d+)$', views.add_article, name='edit_articles'),
How do I have to edit the "get_edit_url" function so the url for edit article to be generated dynamically and to be able to change the path only from urls.py ?
Thank you
This is a common problem, django provides the tools to do it using the reverse function
from django.core.urlresolvers import reverse
def get_edit_url(self):
# assuming your app isn't namespaced
return reverse('edit_articles', args=(self.pk,))
# if your app was namespaced it would be something like
# reverse('articles:edit_articles')
I generally use reverse with model method:
from django.db import models
from django.core.urlresolvers import reverse
class MyModel(models.Model):
...
def get_detail_url(self):
return reverse('article-detail', args=(self.pk,))
def get_list_url(self):
return reverse('article-list')
This approach is more RESTful, detail_view will allow to get,delete,update an instance and list_view will allow to get list or eventually make bulk delete/update.
You could use django-model-urls to do that automatically without even altering your model!
Using your example:
urls.py
url(r'^add/(?P<id>\d+)$', views.add_article, name='edit_articles'),
some template:
{{ url('edit_articles', article) }}
(Note that current version use jingo, raw Django template can use the 0.3.1 version)
Related
I've been trying to create this model object but i keep getting this error:
Field 'id' expected a number but got 'create'.
Image of error is shared bellow.
I am using djangi 3.0.3
view.py file:-
from django.shortcuts import render
from . import models
from django.views.generic import (View,TemplateView,ListView,DetailView,
CreateView,UpdateView,DetailView)
# Create your views here.
class IndexView(TemplateView):
template_name = 'index.html'
class SchoolListView(ListView):
context_object_name = 'schools'
model = models.School
class SchoolDetailView(DetailView):
context_object_name = 'school_detail'
model = models.School
template_name = 'basic_app/school_detail.html'
class SchoolCreateView(CreateView):
fields = ('name','principal','location')
model = models.School
model.py
from django.db import models
from django.urls import reverse
# Create your models here.
class School(models.Model):
name = models.CharField(max_length=265)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("basic_app:detail", kwargs={'pk':self.pk})
class Student(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField()
school = models.ForeignKey(School, related_name='student', on_delete=models.CASCADE)
def __str__(self):
return self.name
output error
Any help will be appreciable
Thank & Regards
Viplav Dube
Click here for code imageIf you are using path, in the urls.py in the application folder follow the code on the picture. This fixed the issue for me.
This looks like an error with your urls.py. Just make sure when you mention URL paths in your urls.py the definitive paths come first and variable paths come later. Here for instance, this code will give you an error:
path('<int:id>/' , view.DetailView.as_view(), name="detail")
path('create/' , view.CreateView.as_view(), name="create")
To avoid the error just change the order like this:
path('create/' , view.CreateView.as_view(), name="create")
path('<int:id>/' , view.DetailView.as_view(), name="detail")
This happens so because in the later piece of code django will look for path /create and if it is unable to match it then it will look for path int:id/. Now since int:id/ is a variable if django was to look for it first it would try to assign "create" to id variable which will give a validation issue.
In appurls.py write this
path('<int:pk>/', views.SchoolDetailView.as_view(), name='detail')
I am learning django by building a simple blogging app. While its all but done, I currently have individual posts having a url in the format https://my_site_dot_com/blog/entry/38/ where the number 38 corresponds to the primary key of said post.
What i want is it to have the format https://my_site_dot_com/blog/entry/this_is_custom_title/ where "this_is_custom_title" corresponds to the heading of the post. I have no idea how to accomplish this. Can anyone offer any assistance?
My model looks like:
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_text = models.TextField()
image = models.FileField(upload_to="media", blank=True)
entry_date = models.DateTimeField(auto_now_add=True)
entry_author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "blog"
def __str__(self):
return self.entry_title
I want the entry_title to the the custom url instead of the primary key.
My urls.py looks like this:
urlpatterns = [
path('', HomeView.as_view(), name="blog-home"),
path('entry/<int:pk>/', EntryView.as_view(), name="entry-detail"),
path('create_entry/', CreateEntryView.as_view(success_url='/'), name='create_entry'),
]
Edit:
The class handing the post looks like this:
class EntryView(DetailView):
model = Entry
template_name = 'blog/entry_detail.html'
data_set = random_info()
stuff_for_post = {
"info": data_set
}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['rand_im'] = random_image()
context['tags'] = ['tag1','tag2','tag3']
return context
I'm an absolute noob in django and come from android/java. So please give an easy to understand explanation.
Thanks in advance
You may add a slug field to your Entry model and a get_absolute_url method. Don't forget to import reverse function from Django's url module.
from django.urls import reverse
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_text = models.TextField()
image = models.FileField(upload_to="media", blank=True)
entry_date = models.DateTimeField(auto_now_add=True)
entry_author = models.ForeignKey(User, on_delete=models.CASCADE)
slug = models.SlugField()
def get_absolute_url(self):
return reverse('entry_detail', kwargs={'slug': self.slug})
class Meta:
verbose_name_plural = "blog"
def __str__(self):
return self.entry_title
Then, within the urls.py module of your app, add the following url pattern to the urlpatterns list. Don't forget to load the corresponding view, I guess it may be EntryView in this case.
from django.urls import path
from .views import EntryView
urlpatterns = [
...
path('<slug:slug>', EntryView.as_view(), name='entry_detail'), # new
...
]
Then the slug should replace the primary key pattern in the url.
To go a bit further, you can use a method within your model that slugify your title for instance. (define the method within the model then call it from the save method of the model, by overriding the save method)
https://docs.djangoproject.com/en/3.0/ref/utils/#django.utils.text.slugify
Currently you are passing an integer through your url. All you need to do is modify this slightly to pass a string through the url. Here is a similar question that discusses how to accomplish this.
As for changes you need to make in your code, urls.py will need to be updated
path('entry/<str:title>/', EntryView.as_view(), name="entry-detail")
You haven't provided your blog post view, but it will then look something like this:
def post(request, title):
template = "template.html"
post = Posts.objects.filter(entry_title==title)
return render(request, template, {'post':post})
If you are using a Class Based View you should use a slug.
First add a new field entry_slug to your Entry model and override the save method in order to automatically generate the entry_slug field:
class Entry(models.Model):
entry_title = models.CharField(max_length=50)
entry_slug = models.CharField(max_length=50)
...
def save(self, *args, **kwargs):
self.entry_slug = slugify(self.entry_title )
super(Entry, self).save(*args, **kwargs)
You can do by replacing the pk with entry_slug:
path('entry/<slug:entry_slug>/', EntryView.as_view(), name="entry-detail")
Really new to Django so bear with me :)
I am running into an issue to display the posts titles in the Django admin.
I have tried both in Python 3
class Post(models.Model):
title = models.TextField(max_length=100)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __str__(self):
return self.title
and Python 2
class Post(models.Model):
title = models.TextField(max_length=100)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __unicode__(self):
return self.title
but unfortunately in the Django admin I see
"Post object "in the list of posts
Thanks in advance for your help.
Maybe you can try this:
from django.utils.encoding import python_2_unicode_compatible
#python_2_unicode_compatible
class Post(models.Model):
title = models.CharField(max_length=255)
text = models.TextField(max_length=10000)
tags = models.TextField(max_length=300)
comments = models.TextField(max_length=400)
def __str__(self):
return self.title
For those who might come here after me, you'd have to add this method inside the model class.
def __str__(self):
return self.title
Make sure it's indented right or else it might not work.
Add your post models to the administration site. Edit the admin.py file of your app and make it look like this:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
The admin has many hooks for customization check documentation
There is no way to have Django return, say, a MyPerson object whenever you query for Person objects. A queryset for Person objects will return those types of objects. The whole point of proxy objects is that code relying on the original Person will use those and your own code can use the extensions you included (that no other code is relying on anyway
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
Know more
For those who come after, make sure post is in lowercase.
And after which make migrations again.
make sure name='post' not 'Post'...
migrations.AlterModelOptions(
name='post',
I'm hoping this is just an issue of my poor regex understanding.
I'm attempting to use the exact code on Django 1.9's generic views to build a blog and personal site, and, down to the testing, here's where I run into trouble:
def test_post_page(self):
post = PostModelFactory()
first_post = Post.objects.all()[0]
post_url = first_post.get_absolute_url()
print(post_url)
response = self.client.get(post_url, follow=True)
self.assertEqual(response.status_code, 200)
So, through that print statement, I determined models.Post.get_absolute_url() was returning my homepage URL. Here's models.py:
class Post(models.Model):
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=200, default="")
pub_date = models.DateTimeField(auto_now_add=True)
text = models.TextField()
slug = models.SlugField(max_length=40,unique=True)
def get_absolute_url(self):
return "%s/" % (self.slug)
Should it come up, I copied down what the generic views documentation has, so my Detailview in /blog/urls.pyis as follows:
url(r'^(?P<slug>[-\w]+)/$', PostDetailView.as_view(), name='post-detail'),
Same of views.py:
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
As far as I can tell, my get_absolute_url() function is simply not doing what I think it's doing, let alone what the regex in urls.py expects it to do.
Also: Is there anyone who can fully explain how slugfield works? I know it's a keyword generator to create a url, but I'm not sure how it works (or doesn't, as in this example).
Then, finally, in both a tutorial that I'm quasi-following alongside the documentation, and the documentation, itself, I'm not fully understanding where the variable names in templates are coming from (my understanding is that the request hit's the URL, which generates the data from views.py). The "ListView" object in the template shares the model name, "post" (or "article" in the documentation), where its pageview at the bottom is accessed simply through "page_obj", and the "DetailView" object is simply called "object". I also may be having a problem with paginating my ListView, ( which is identical to the documentation example, but with the extra line paginate_by = 2 right above get_context_data.
Thank you.
EDIT:
I've included PostModelFactory:
class PostModelFactory(DjangoModelFactory):
class Meta:
model = Post()
django_get_or_create = (
'title',
'subtitle',
'text',
'pub_date',
)
title = 'This is a test.'
subtitle = 'This is only a test.'
text = 'Madness? This is Sparta.'
pub_date = timezone.now()
def __init__(self):
self.save()
Edit: The issue turned out to be the lack of a slug in the PostModelFactory.
Ideally, you should use reverse in get_absolute_url, instead of hardcoding it.
from django.core.urlresolvers import reverse
class Post(models.Model):
...
def get_absolute_url(self):
return reverse('post-detail', args=[self.slug])
If you do hardcode the URL, it should contain a leading slash.
def get_absolute_url(self):
return "/%s/" % (self.slug)
If first_post.get_absolute_url is returning the homepage url with your current get_absolute_url, that suggests that the slug is an empty string.
I've another quick question, I need to use very often a specific html representation for a model field. I know there is a lot of documentation for forms but what about simple model field ? For instance :
models.py
class Status (models.Model):
order = models.PositiveIntegerField(_(u'Order'))
name = models.CharField(_(u'Name'), max_length=255, unique=True, db_index=True)
color = models.CharField(_(u'Color'), max_length=6, null=True, blank=True)
template.html
{{status.order.as_span}} will be equivalent to <span>{{status.order}}<span>
The first idea I have is to implement a function inside a manager but seems to break the MVC rules ... There is a proper way for this ?
You shouldn't assemble html in your view code, this is what template tags are for. Put this in eg. <app>/templatetags/<app>tags.py (replace <app> with your app name):
from django.utils.safestring import mark_safe
from django import template
register = template.Library()
#register.simple_tag
def spanme(fld):
return mark_safe("<span>%s</span>" % fld)
and use it as:
{% load <app>tags %}
...
{% spanme status.order %}
It is possible to subclass the model field also:
from django.utils.safestring import mark_safe
from django.db.models.fields import CharField
from django.db import models
class SpanValue(unicode):
def as_span(self):
return mark_safe(u"<span>%s</span>" % self)
class SpanField(CharField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if value is None:
return None
return SpanValue(value)
class SpanModel(models.Model):
foo = SpanField(max_length=25)
then you could use it like this:
from django import template
from maint.models import *
#val = SpanModel.objects.create(foo='bar')
val = SpanModel.objects.get(foo='bar') # must get it from the db for `to_python` to run
t = template.Template(u"""{{ val.foo.as_span }}""")
print t.render(template.Context({'val': val}))
which would print
<span>bar</span>
Doing something like this would be very surprising to anyone else, so please never ever do this :-)
ps: if you inherit from something that isn't a text field you might be able to override __unicode__ (so you wouldn't have to use .as_span in the template), however that is considerably more work.