Access url parameters, extra options and define optional url parameters on Django view methods

Problem

You want to access url parameters, extra options and define optional url parameters inside a Django view method

Solution

Use regular expressions and Django's special syntax to handle url parameters or the url options dictionary to define url extra options. Use default values in Django view methods to define optional url parameters. Use the HTTP request object in view methods to gain access to parameters.

How it works

In a previous recipe you learned how Django uses regular expressions to match url patterns and send requests to a Django view method. This exercise though was limited to discarding the url information after the initial pattern match. If you didn't do this last exercise see the recipe 'Set up content, understand Django urls, templates and apps'.

Sometimes it's helpful or even necessary to pass url information to the Django view method. For example, if you have several urls like /stores/1, /stores/2 and /stores/3, the last part of the url illustrates a pattern. In this case 1, 2 and 3 refer to store ids. It can be helpful or necessary to relay this url information to the Django view method so it can get more information about the store id (e.g. query a database), information that can then be passed to a Django template for presentation. This process requires that part of the url be treated as a parameter.

To handle url parameters Django uses a special syntax in combination with regular expressions. Listing 1 shows a url example that creates a parameter named store_id.

Listing 1 - Django url parameter definition

from coffeehouse.stores import views as stores_views

urlpatterns = patterns[
    url(r'^stores/(?P<store_id>\d+)/',stores_views.detail),
]

Notice the (?P<store_id>\d+) syntax in listing 1. The ?P tells Django to convert the matching value to a parameter and <store_id> assigns the matching value to a variable called store_id. The final piece of the url parameter syntax \d+ is a regular expression to determine the matching value, in this case the matching value are digits (i.e.numbers).

The important functionality of the syntax in listing 1 is the matching url value is treated as a parameter. For example, for the url /stores/1 the value 1 is assigned to the store_id parameter, for the url /stores/2 the value 2 is passed to the store_id parameter. For a url like /stores/downtown the regular expression pattern doesn't match -- because downtown are letters not digits -- so no action is taken.

If a url match occurs for listing 1, the request is sent directly to the Django view method coffeehouse.stores.views.detail. Where coffeehouse.stores is the package name, views.py the file inside the stores app and detail the name of the view method. Listing 2 illustrates the Django view method to access the store_id parameter.

Listing 2 - Django view method in views.py to access url parameter

from django.shortcuts import render

def detail(request,store_id):
    # Access store_id with 'store_id' variable
    return render(request,'stores/detail.html')

Notice in listing 2 how the detail method has two arguments. The first argument is a request object, this is always the same for all Django view methods that handle url requests. The second argument is the parameter passed by the url. It's important to note the names of url parameters must match the names of the method arguments. In this case notice in listing 1 the parameter name is store_id and in listing 2 the method argument is also named store_id.

With access to the url parameter via the view method argument, the method can execute logic with the parameter (e.g. query a database) that can then be passed to a Django template for presentation. In the next recipe I'll describe how to pass data from a view method to a Django template.

Url parameters can also be made optional to leverage the same view method multiple times. Parameters can be made optional by assigning a default value to a view method argument. Observe listing 3 which includes a new url that calls the same view method (coffeehouse.stores.views.detail) but doesn't define a parameter.

Listing 3 - Django urls with optional parameters


from coffeehouse.stores import views as stores_views

urlpatterns = patterns[
    url(r'^stores/',stores_views.detail),
    url(r'^stores/(?P<store_id>\d+)/',stores_views.detail),
]

If you called the url stores without modifying the detail method in listing 2 you would get an error. The error occurs because the detail view method expects a store_id argument which isn't provided by the url. To fix this problem, you can define a default value for the store_id as illustrated in listing 4.

Listing 4 - Django view method in views.py with default value

from django.shortcuts import render

def detail(request,store_id=1):
    # Access store_id with 'store_id' variable
    return render(request,'stores/detail.html')

Notice in listing 4 how the store_id argument has an assignment =1. This means the argument will have a default value of 1 in case the view method is called without store_id. This approach allows you to leverage the same view method to handle multiple urls with optional parameters.

In addition to accessing url parameters inside view methods, it's also possible to access extra options from the url definition. These extra options are defined inside a general purpose dictionary declared as a third argument in a url definition. After the view method declaration, you add a dictionary with the key-value pairs you wish to access inside the view method.

For example, you can change the url in listing 3 from url(r'^stores/',stores_views.detail), to url(r'^stores/',stores_views.detail,{'location':'headquarters'}). The location key becomes a url extra option that's passed as a parameter to the view method.

Url extra options are assigned like url parameters. So to access a url extra option inside a view method you'll need to modify the method signature to accept an argument with the same name as the url extra option. In this case, the method signature def detail(request,store_id=1): would need to change to def detail(request,store_id=1,location=None): -- notice the location argument is made optional by assigning a default value of None.

Finally, it's also possible to access url parameters separated by ? and & inside Django view methods. On certain urls -- those made by HTTP GET requests -- parameters are added to a url with ? followed by parameter_name=parameter_value separated by &. These type of parameters can be accessed easily inside a view method using the request object, instead of attempting to use a url regular expression.

Take for example the url /stores/1/?hours=sunday&map=flash. The 1 value can easily be assigned to a parameter by a url regular expression, but the remaining parameters can be very difficult to assign with a regular expression. Since Django urls just match patterns, the arguments separated by ? and & aren't altered and are accessible in a view method's request object. Listing 5 illustrates how to extract arguments separated by ? and & using request.GET.

Listing 5 - Django view method extract url parameters with request.GET

from django.shortcuts import render

def detail(request,store_id=1,location=None):
    # Access store_id param with 'store_id' variable and location param with 'location' variable
    # Extract 'hours' or 'map' value appended to url as
    # ?hours=sunday&map=flash
    hours = request.GET.get('hours', '')
    map = request.GET.get('map', '')
    # 'hours' has value 'sunday' or '' if hours not in url
    # 'map' has value 'flash' or '' if map not in url
    return render(request,'stores/detail.html')

Listing 5 uses the syntax request.GET.get(<parameter>, ''). If the parameter is present in request.GET it extracts the value and assigns it to a variable for further usage, if the parameter is not present then the parameter variable is assigned an empty value of ''. This last process is designed to extract parameters from an HTTP GET request, however Django also support the syntax request.POST.get to extract parameters from an HTTP POST request.