Cheng
Cheng

Reputation: 17954

Django url pattern for DetailView

I was following this tutorial

Here is the model:

class Post(models.Model):
    title = models.CharField(max_length=200)
    pub_date = models.DateTimeField()
    text = models.TextField()
    slug = models.SlugField(max_length=40, unique=True)

    def get_absolute_url(self):
        return "/{}/{}/{}/".format(self.pub_date.year, self.pub_date.month, self.slug)

    def __unicode__(self):
        return self.title

    class Meta:
        ordering = ['-pub_date']

Here is the url for accessing a post:

from django.conf.urls import patterns, url
from django.views.generic import ListView, DetailView
from blogengine.models import Post

urlpatterns = patterns('',

    url(r'^(?P<pub_date__year>\d{4})/(?P<pub_date__month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
        model=Post,
    )),
)

What's interesting about this code is the use of double underscore in the url pattern. I did lookup django's documentation on url pattern: https://docs.djangoproject.com/en/1.8/topics/http/urls/ But I cannot find any docs on using double underscore inside url pattern.

My guess about this usage is that a keyword argument called pub_year will be passed to the view function of DetailView, and the pub_year argument has two attributes year and month.

I tried to use the following url and it still worked:

url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
    model=Post,
)),

So I guess the use of double underscore is not necessary.

I found this line in Django's source code

It looks like a detailview (which inherits from SingleObjectMixin) can use slug to match a record. If that is the case, then the year and month arguments are not needed.

So here are my questions:

  1. Is there any value in using double underscore in url pattern?
  2. When I reduce the url pattern to the following, I only get a 404 when requesting the page with: 127.0.0.1:8000/test/ (test is the slug for an existing record stored in db)

    url(r'^/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(
    model=Post,
    )),   
    

why is that?

Upvotes: 1

Views: 1351

Answers (2)

dhke
dhke

Reputation: 15408

Since this has not really been answered, yet:

  1. Is there any value in using double underscore in url pattern?

DRY, more or less:

class SomeDetailView(...):
     def get_queryset(self):
         queryset = super(SomeDetailView, self).get_queryset()
         # grab all named URL keyword arguments and 
         # transform them into field lookup patterns.
         # Leave out slug and primary key keywords, though.
         filter_args = {
              k: v for k, v in self.kwargs.iteritems()
              if k not in (self.slug_url_kwarg, self.pk_url_kwarg)
         }
         if filter_args:
             queryset = queryset.filter(**filter_args)
         return queryset

Since e.g. pub_date__year is a valid field lookup you --possible security problems nonwithstanding-- just gained the ability to add lookup criteria solely via named capture patterns in urls.py.

  1. When I reduce the url pattern to the following, I only get a 404 when requesting the page with: 127.0.0.1:8000/test/ (test is the slug for an existing record stored in db)
url(r'^/(?P<slug>[a-zA-Z0-9-]+)/?$', DetailView.as_view(model=Post, )),   
       ^ leading slash 

That's common enough mistake that it made it into documentation:

There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.

Try again with r'^(?P<slug>[a-zA-Z0-9-]+)/?$'

Documentation is, however, a little misleading, here. "There's no need" should be read as "The initial leading slash is matched automatically and is not part of the url pattern".

Upvotes: 1

bwarren2
bwarren2

Reputation: 1397

Those ?Ps are just capturing patterns in a regex; they are names and can be whatever you want, but will be passed as keyword arguments. They need a <name> (ex (?P<name>) ) or they will be passed as regular arguments, which is why your reduced example failed.

Personally I reserve __s for db lookups, and would use something like (?<pub_year>) as a more descriptive variable name.

There are default conventions in class-based-views that let you use standard names to write really terse code. In DetailView, 'slug' is the default slug_field and slug_url_kwarg; if you trace the dispatch() pattern through, you will see where those defaults can be tweaked/how they are used. BTW: CCBV is really useful and you should use it, and your tutorial link appears broken.

Upvotes: 1

Related Questions