25
votes

Can someone please explain how you can write a url pattern and view that allows optional parameters? I've done this successfully, but I always break the url template tag.

Here's what I have currently:

Pattern

(r'^so/(?P<required>\d+)/?(?P<optional>(.*))/?$', 'myapp.so')

View

def so(request, required, optional):

If I use the url template tag in this example providing both arguments, it works just fine; however, if I omit the optional argument, I get a reversing error.

How can I accomplish this?

Thanks, Pete

7
It is on the roadmap for 1.3: code.djangoproject.com/ticket/14772 - user580640

7 Answers

38
votes

I generally make two patterns with a named url:

url(r'^so/(?P<required>\d+)/$', 'myapp.so', name='something'),
url(r'^so/(?P<required>\d+)/(?P<optional>.*)/$', 'myapp.so', name='something_else'),
18
votes

Django urls are polymorphic:

url(r'^so/(?P<required>\d+)/$', 'myapp.so', name='sample_view'),
url(r'^so/(?P<required>\d+)/(?P<optional>.*)/$', 'myapp.so', name='sample_view'),

its obious that you have to make your views like this:

def sample_view(request, required, optional = None):

so you can call it with the same name and it would work work url resolver fine. However be aware that you cant pass None as the required argument and expect that it will get you to the regexp without argument:

Wrong:

{% url sample_view required optional %}

Correct:

{% if optional %}
    {% url sample_view required optional %}
{% else %}
    {% url sample_view required %}
{% endif %}

I dont know whether this is documented anywhere - I have discovered it by accident - I forgot to rewrite the url names and it was working anyway :)

5
votes

Others have demonstrated the way to handle this with two separate named URL patterns. If the repetition of part of the URL pattern bothers you, it's possible to get rid of it by using include():

url(r'^so/(?P<required>\d+)/', include('myapp.required_urls'))

And then add a required_urls.py file with:

url(r'^$', 'myapp.so', name='something')
url(r'^(?P<optional>.+)/$', 'myapp.so', name='something_else')

Normally I wouldn't consider this worth it unless there's a common prefix for quite a number of URLs (certainly more than two).

1
votes

Why not have two patterns:

(r'^so/(?P<required>\d+)/(?P<optional>.*)/$', view='myapp.so', name='optional'),
(r'^so/(?P<required>\d+)/$', view='myapp.so', kwargs={'optional':None}, name='required'),
1
votes

For anyone who is still having this problem. I use Django 1.5 (updated: using 1.8) and it's still working fine.

I use:

urls.py

url(r'^(?P<app_id>\d+)/start/+(?P<server_id>\d+)?', views.restart, name='restart')

Then when I want to have the two urls

/1/start/2

and

/1/start

I use:

{% url '<namespace>:start' app.id %}
{% url '<namespace>:start' app.id server.id %}

This will create the urls

/1/start/2 and 
/1/start/ <- notice the slash.

If you create a url manually you have to keep the / in mind.

I hoop this helps anyone!

-1
votes

in views.py you do simple thing.

def so(request, required, optional=None): 

And when you dont get optional param in url string it will be None in your code.

Simple and elegant :)

-2
votes

Depending on your use case, you may simply want to pass a url parameter like so:

url/?parameter=foo

call this in your view:

request.REQUEST.get('parameter')

this will return 'foo'