In chapter 1 you learned about the core building blocks in Django, inclduing what are views, models and urls. In this chapter, you'll learn more about Django urls which are the entry point into a Django application workflow.You'll learn how to create complex url regular expressions, how to use url values in view methods & templates, how to structure & manage urls and how to name urls.

After urls, Django views represent the next step in almost all Django workflows, where views are charged with inspecting requests, executing business logic, querying a database and validating data, as well as generating responses. In this chapter, you'll learn how to create Django views with optional parameters, the structure of view requests & responses, how to use middleware with views and how to create class based views.

Url regular expressions

Regular expressions provide a powerful approach in all programming languages to determine patterns. However, with power also comes complexity, to the point there are entire books written on the topic of regular expressions[1].

Although most Django urls will never exceed a fraction of the complexity illustrated in many regular expression books, it's important that you understand some of the underlying behaviors and most common patterns of regular expressions in Django urls.

Precedence rule: Granular urls first, broad urls last

Django urls need to follow a certain order and syntax to work correctly. Broad url regular expressions should be declared last and only after more granular url regular expressions.

This is because Django url regular expression matching doesn't use short-circuiting behavior, like a nested conditional statement (e.g. if/elif/elif/elif/else) were as soon as one condition is met, the remaining options are ignored. In Django urls if there's more than one matching regular expression for an incoming url request, it will be the top-most one's action that gets triggered. Precedence for matching url regular expressions is given from top (i.e. first declared) to bottom (i.e. last declared).

You shouldn't underestimate how easy it can be to introduce two url regular expressions that match the same pattern, particularly if you've never done regular expressions since the syntax can be cryptic. Listing 2-1 illustrates the right way to declare Django urls, with more granular regular expressions toward the top and broad regular expressions toward the bottom.

Listing 2-1. Correct precedence for Django url regular expressions

from django.views.generic import TemplateVieww
urlpatterns = [
    url(r'^about/index/',TemplateView.as_view(template_name='index.html')),
    url(r'^about/',TemplateView.as_view(template_name='about.html')),
]

Based on listing 2-1, lets walk through what happens if Django receives a request for the url /about/index/. Initially Django matches the last regular expression, which says 'match ^about/'. Next, Django continues upward inspecting the regular expressions and reaches 'match ^about/index/' which is an exact match to the request url /about/index/ and therefore triggers this action to send control to the index.html template.

Now let's walk through a request for the url /about/. Initially Django matches the last regular expression which says 'match ^about/'. Next, Django continues upward inspecting the regular expressions for a potential match. Because no match is found -- since 'match ^about/index/' is a more granular regular expression -- Django triggers the first action to send control to the about.html template which was the only regular expression match.

As you can see, listing 2-1 produces what can be said to be expected behavior. But now let's invert the order of the url regular expressions, as shown in listing 2-2, and break down why declaring more granular regular expressions toward the bottom is the wrong way to declare Django url regular expressions.

Listing 2-2. Wrong precedence for Django url regular expressions

from django.views.generic import TemplateVieww
urlpatterns = [
    url(r'^about/',TemplateView.as_view(template_name='about.html')),
    url(r'^about/index/',TemplateView.as_view(template_name='index.html')),
]

The issue in listing 2-2 comes when a request is made for the url /about/index/. Initially Django matches the last regular expression, which says 'match ^about/index/'. However, Django continues inspecting the regular expressions and reaches 'match ^about/' which is a broader match to the request url /about/index/, but nevertheless a match! Therefore Django triggers this action and sends control to the about.html template, instead of what was likely expected to be the index.html template from the first match.

Exact url patterns: Forgoing broad matching

In the past section, I intentionally used regular expressions that allowed broad url matching. In my experience, as a Django project grows you'll eventually face the need to use this type of url regular expressions -- but more on why this is so, shortly.

As it turns out, it's possible to use exact url regular expressions. Exact url regular expressions remove any ambiguity introduced by the order in which Django url regular expression are declared.

Lets rework the url regular expressions from listing 2-2 and make them exact regular expressions so their order doesn't matter. Listing 2-3 illustrates exact regular expressions on basis of those in listing 2-2.

Listing 2-3. Exact regular expressions, where url order doesn't matter.

from django.views.generic import TemplateVieww
urlpatterns = [
    url(r'^about/$',TemplateView.as_view(template_name='about.html')),
    url(r'^about/index/$',TemplateView.as_view(template_name='index.html')),
]

Notice the regular expressions in listing 2-3 end with the $ character. This is the regular expression symbol for end of line, which means the regular expression urls only match an exact pattern.

For example, if Django receives a request for the url /about/index/ it will only match the last regular expression in listing 2-3 which says 'match ^about/index/$'. However, it won't match the higher-up ^/about/$ regular expression because this regular expression says match about/ exactly with nothing else after, since the $ indicates the end of the pattern.

However, as useful as the $ character is to make stricter url regular expressions, it's important you analyze its behavior. If you plan to use url Search Engine Optimization (SEO), A/B testing techniques or simply want to allow multiple urls to run the same action, stricter regular expression with $ eventually require more work.

For example, if you start to use urls like /about/index/, /about/email/,/about/address/ and they all use the same template or view for processing, exact regular expressions just make the amount of urls you declare larger. Similarly, if you use A/B testing or SEO where lengthier variations of the same url are processed in the same way (e.g. /about/landing/a/, /about/landing/b/, /about/the+coffeehouse+in+san+diego/) broad url matching is much simper than declaring exact url patterns.

In the end, whether you opt to use exact url regular expression ending in $, I would still recommend you maintain the practice of keeping finer grained url regular at the top and broader ones at the bottom, as this avoids the unexpected behaviors described in listing 2-2 when more than one regular expression matches a url request.

Common url patterns

Although url regular expressions can have limitless variations -- making it next to impossible to describe each possibility -- I'll provide examples on some of the most common url patterns you're more likely to use. Table 2-1 shows individual regular expression characters for Django urls and table 2-2 shows a series of more concrete examples with url patterns.

Table 2-1. Regular expression syntax for Django urls: Symbol (Meaning)

^ (Start of url)$ (End of url)\ (Escape for interpreted values)| (Or)
+ (1 or more occurrences)? (0 or 1 occurrences){n} (n occurrences){n,m} (Between n and m occurrences)
[] (Character grouping)(?P___) (Capture occurrence that matches regexp ___ and assign it to name. (Any character)\d+ (One or more digits). Note escape, without escape matches 'd+' literally]
\D+ (One or more non-digits).Note escape, without escape matches 'D+' literally][a-zA-Z0-9_]+ (One or more word characters, letter lower-upper case, number or underscore)\w+ (One or more word characters, equivalent to [a-zA-Z0-9_]) Note escape, without escape matches 'w+' literally][-@\w]+ (One or more word character, dash or at sign). Note no escape for \w since it's enclosed in brackets (i.e. a grouping)

Table 2-2. Common Django url patterns and their regular expressions, with samples

Url regular expressionDescriptionSample urls
url(r'^$',.....)Empty string (Home page)Matches:
http://127.0.0.1/
url(r'^stores/',.....)Any trailing charactersMatches:
http://127.0.0.1/stores/
http://127.0.0.1/stores/long+string+with+12345
url(r'^about/contact/$',.....)Exact, no trailing charactersMatches:
http://127.0.0.1/about/contact/
Doesn't match:
http://127.0.0.1/about/
url(r'^stores/\d+/',....)NumberMatches:
http://127.0.0.1/stores/2/
http://127.0.0.1/stores/34/
Doesn't match:
http://127.0.0.1/stores/downtown/
url(r'^drinks/\D+/',.....)Non-digitsMatches:
http://127.0.0.1/drinks/mocha/
Doesn't match:
http://127.0.0.1/drinks/324/
url(r'^drinks/mocha|espresso/',.....)Word options, any trailing charactersMatches:
http://127.0.0.1/drinks/mocha/
http://127.0.0.1/drinks/mochaccino/
http://127.0.0.1/drinks/espresso/
Doesn't match:
http://127.0.0.1/drinks/soda/
url(r'^drinks/mocha$|espresso/$',.....)Word options exact, no trailing charactersMatches:
http://127.0.0.1/drinks/mocha/
Doesn't match:
http://127.0.0.1/drinks/mochaccino/
Matches:
http://127.0.0.1/drinks/espresso/
Doesn't match:
http://127.0.0.1/drinks/espressomacchiato/
url(r'^stores/\w+/',.....)Word characters (Any letter lower-upper case, number or underscore)Matches:
http://127.0.0.1/stores/sandiego/
http://127.0.0.1/stores/LA/
http://127.0.0.1/stores/1/
Doesn't match:
http://127.0.0.1/san-diego/
url(r'^stores/[-\w]+/',.....)Word characters or dashMatches:
http://127.0.0.1/san-diego/
url(r'^state/[A-Z]{2}/',.....)Two upper case lettersMatches:
http://127.0.0.1/CA/
Doesn't match:
http://127.0.0.1/Ca/
Django urls don't inspect url query strings

On certain urls -- those made by HTTP GET requests, common in HTML forms or REST services -- parameters are added to urls with ? followed by parameter_name=parameter_value separated by & (e.g./drinks/mocha/?type=cold&size=large). These set of values are known as query strings and Django ignores them for the purpose of url pattern matching.

If you need to make use of these values as url parameters -- a topic explored in the next section -- you can access these values in Django view methods through the request reference. Another alternative is to change the url structure to accommodate regular expressions (e.g. /drinks/mocha/cold/large/ instead of /drinks/mocha/?type=cold&size=large.)

  1. http://www.apress.com/la/book/9781590594414