URL patterns: missing conditional parameters don't get the default value specified in the view, but why?

Hi,

When I use a conditional, named capture group in a URL pattern with a view function which has a default value, the default value is not adhered to. For a full example, this minimal Django app below will act like the following:

  • On visiting /this, it will print “this” (as expected).
  • On visiting /that, it will print “that” (as expected).
  • On visiting / (=not specifying anything), instead of falling back to the default view parameter “this”, I get None.
    import sys

    from django.conf import settings
    from django.conf.urls import url 
    from django.core.management import execute_from_command_line
    from django.http import HttpResponse

    settings.configure(
        DEBUG=True,
        ROOT_URLCONF=__name__,
    )


    def routing_example(request, choices="this"):
        return HttpResponse('<html><body>{text}</body></html>'.format(text=choice))

    urlpatterns = [
        url(r'^(?:(?P<choice>(this|that)))?$', routing_example),
    ]

    if __name__ == '__main__':
        execute_from_command_line(sys.argv)

This was something that I previously raised as bug ticket in https://code.djangoproject.com/ticket/31809#ticket, but I don’t fully understand felixxm’s response who said this is the expected behavior. Could someone please explain?

The ‘?’ operator in a regex pattern makes the part of the pattern optional. Because of this, it appears that if you don’t guarantee a choice is passed in, it will default to ‘None.’

As felixxm said, you should use two different urls for simplicity’s sake.

Also, is there a reason you’re using urls as opposed to paths?

This topic was also brought up just a couple weeks ago - see the discussion at Why is the "default value" in view not working ?

Ken

Thanks for your reply, I really appreciate it! I understand it well that the part of the regex pattern is optional - that was completely intentional. I also understand that it defaults to None - my question is, why does it default to that? I was assuming that during processing such a regex pattern, if any capture groups would end up being missing and resulting in a None, that None would not be used for overriding the default value on the view.

About your question why urls is used, the reason is simply that we ran into this issue on an old Django 1.8 app, so when I originally thought this might be a bug, I started to test the exact same snippet up to the current version - it acted the same way of course.

Ken, I don’t think that this is exactly the same thing. In my case, the pattern is matching perfectly, so the view is loaded just as expected.

The standard Python regex library states that a regex search returns None if no match is found. This assigns the string “None” to the parameter value “choice” within the url. Since a parameter is supplied, it overrides the default parameter supplied in the function call.

This makes sense to me, because in my mind, if I define a url with a component of that url being assigned to a variable, I should be able to trust that there will be a value assigned to that variable. If I have a variable named “choice”, defined as a string, my view should be able to rely upon the fact that I will receive a string in that variable.

The fundamental pattern for using two entries to cover the case of no parameter being supplied is documented at Specifying defaults for view arguments.

The standard Python regex library states that a regex search returns None if no match is found. This assigns the string “None” to the parameter value “choice” within the url.

Yes, and for the regex part, returning None is exactly what I expect. But why would that get assigned as a string “None”? A value which is “denoting lack of value” should not suddenly become a “real” value…

Because that’s part of the contract between the URL resolver and the view dispatcher.
By having a named component in the URL, you’re making a promise to the view that you will be passing a variable to it by that name. And so the dispatcher will create a variable of the specified type to pass to the view.

If you want to handle conditional components within the view, you match on the “short version” of the URL and parse the rest of the URL yourself in the view - effectively writing your own dispatcher.