1

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?

styvane
  • 59,869
  • 19
  • 150
  • 156
Cheng
  • 16,824
  • 23
  • 74
  • 104
  • `DetailView` by default supports only lookup by primary key and slug. `pub_date__year` and `pub_date__month` are however valid keyword argument names to `QuerySet.filter()`. Seems like this is an artifact of more elaborate functionality that is left over in the code shown in the tutorial. – dhke Jun 07 '15 at 15:20

2 Answers2

1

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.

Community
  • 1
  • 1
bwarren2
  • 1,347
  • 1
  • 18
  • 36
  • Thanks for your explaination but you didn't answer the two questions that I asked. I didn't ask what those ?Ps are as I already know what they are. Please see my questions at the bottom. – Cheng Jun 08 '15 at 02:18
  • TLDR: 1. No, don't do it. 2. Michael's edit changed your code. If you are not using the capturing pattern, you are changing the named variable the CBV gets to an unnamed one, which will probably break things. If your code has the capturing expression, maybe your urls are namespaced differently than you think? Try using reverse() on your URL on the CLI to confirm that the urlconf is formatted how you think. – bwarren2 Jun 08 '15 at 14:17
1

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".

dhke
  • 15,008
  • 2
  • 39
  • 56