CHAPTER 4

image

URLs and Views

Much of this book is split into fairly self-contained chapters, but this one covers two seemingly unrelated concepts together, because each relies very much on the other. URLs are the primary entry points to your site, while views are the code that respond to incoming events. What goes on in a view is very open-ended. Aside from accepting a request and returning a response, there’s no particular protocol that views should adhere to, and no rules about what they are or aren’t allowed to do.

The possibilities for views are too vast to consider describing in detail, and there aren’t any utilities designed explicitly for views to use while executing. Instead, it’s possible to hook into the process Django uses to map Web addresses to the views they should execute. This makes the link between URLs and views extremely important, and a thorough understanding of it can enable further advanced techniques.

Also, in terms of how Django manages incoming requests, URL configurations exist solely to dispatch a request to a view that can handle it. Discussing URLs and URL configurations independently of views would be of little value.

URLs

Since all incoming requests to a Web server originate with the Web browser accessing a URL, a discussion of URLs is an important place to start. The process taken by the browser to transform a URL into a message to be sent to the Web server is beyond the scope of this chapter, but Chapter 7 provides more information.

One common point of confusion is whether a Web address should be called a Uniform Resource Identifier (URI) or a Uniform Resource Locator (URL). Many people use these two terms interchangeably, regardless of whether they know the difference. In a nutshell, a URI is a complete addressing mechanism that includes two pieces of information.

  • The name of the scheme or protocol to be used to connect to the resource. This is always followed by a single colon.
  • The path where the resource can be found. The exact format of this path may be different for different schemes, so not all URI paths look alike.

URLs, on the other hand, are addresses from a small set of connection schemes whose path portions all conform to a single format. Included in this set are such common protocols as HTTP, HTTPS and FTP—essentially the common protocols found on the Web today. The path format shared by these protocols is as follows.

  • The protocol to be used to access the resource, such as http:// for standard HTTP. This is a slight extension to the scheme portion of the URI because it is assumed that all URL protocols will include two forward slashes following the colon.
  • The host domain where the resource can be found, such as prodjango.com or www.prodjango.com .
  • Optionally, the port number the server responds to. Each protocol has a default port that will be used if one isn’t supplied. For standard HTTP, this is 80, while for encrypted HTTP using the Secure Sockets Layer (SSL), it will be 443.
  • The path of the resource on the server, such as /chapter4/.

So while all URLs are certainly URIs, not all URIs are URLs. That subtle distinction can be confusing when working on the Web because either term can be used to describe the addresses found everywhere. Since Django is built for the Web—and thus the addresses covered under URL schemes—the rest of this book will refer to these addresses as URLs, as the full range of URIs might not be suitable for Django’s dispatching mechanism.

DESIGNING CLEAN URLS

In an ideal world, the URLs you choose when setting up a site the first time will never change,1 remaining intact until the documents—or the entire server—are no longer maintainable. Changing URLs simply because of a redesign or reorganization of the site is generally bad form and should be avoided.

The key to making URLs maintainable for the long haul and making it easier for your users to keep track of them, is to design them well in the first place. Django makes this easy, allowing you to design your URLs in whatever hierarchy you like, assigning variables right in the URL and splitting the URL structure into manageable chunks.

Above all, URLs are part of your application’s user interface, since users have to see them, read them and often type them in manually. Keep this in mind when designing your URLs.

Standard URL Configuration

Django doesn’t provide any features for automatically discovering or generating a URL structure for any site. Instead, each site and application is expected to explicitly declare whatever addressing scheme is most appropriate using URL configurations. This isn’t a limitation—it’s a feature that allows you to define your site’s addresses the way you’d like. After all, sites on the Web are like real estate; your Web framework shouldn’t determine your floor plan.

Defining a URL configuration may seem quite simple, but there’s a bit going on that merits some special attention, especially since Django’s own tools aren’t the only way to define this configuration. The implementation lives at django.conf.urls.defaults and provides two functions that work together to manage URL configurations.

The patterns() Function

A URL configuration consists of a list of patterns that each map a particular type of URL to a view. These patterns each have a few components but all of them are specified together as arguments to the patterns() function.

from django.conf.urls.defaults import *
 
urlpatterns = patterns('',
    (r'^$', 'post_list'),
    (r'^(?P<id>d+)/$', 'post_detail'),
    (r'^(?P<id>d+)/comment/$', 'post_comment', {'template': 'comment_form.html'}),
)

The arguments for this function can be placed in two groups:

  • A single import path prefix for any views that are specified as strings
  • Any number of URL patterns

Historically, all views were specified as strings, so the prefix was a great way to reduce the amount of duplication required to map URLs to views from a single application. More recently, URL patterns are allowed to specify views as callables, in which case the prefix would be ignored. It is still often useful to specify views as strings using a prefix, as it reduces the overall code by not requiring a set of imports for the views.

The URL patterns are traditionally passed in as tuples, though “The url() Function” section later in this chapter describes a more recent addition. Details of each portion of this tuple are as follows:

  • A regular expression used to match against the URL
  • The view function to be called for a request matching this pattern
  • Optionally, a dictionary of arguments to be passed to the function

This tuple contains all the information necessary to map an incoming request to a view function. The URL’s path will be checked against the regular expression, and if a match is found, the request is passed along to the specified view. Any arguments captured by the regular expression are combined with the explicit arguments in the extra dictionary, then passed along to the view along with the request object.

image Note  Like most regular expressions, URL patterns are typically described using raw strings, indicated by the r prefix. Raw strings don’t go through standard escaping, which is useful here because regular expressions offer their own forms of escaping. If we didn’t use raw strings, we’d have to escape each backslash in order to pass it through to the regular expression. The example here would be written '^(?P<id>\d+)/$' without a raw string.

MULTIPLE ARGUMENTS WITH THE SAME NAME

A single URL configuration can provide values in two separate ways: in the URL’s regular expression and in the dictionary attached to the pattern. Accepting arguments from two different sources makes it possible to provide two different values for the same key, which needs to be resolved somehow. If you try doing this with keyword arguments to a standard function, Python will raise a TypeError as described in Chapter 2.

Django allows these multiple arguments to be specified without raising an exception, but they can’t be all passed to the view together. As the second portion of this chapter shows, views are called just like any normal Python function, so these multiple arguments would cause the same TypeError described in Chapter 2. To resolve this issue without an error, Django has to reliably choose one instead of the other. Any argument provided with a dictionary in the URL configuration will take priority over anything found in the URL.

It’s bad form to provide multiple arguments with the same name in this manner, since it relies heavily on Django’s handling of the situation to work properly. While that behavior isn’t likely to change on a whim, relying on it could cause problems in the future. More importantly, specifying the same argument name in multiple places greatly reduces the readability of your URL configurations. Even in closed-source applications, someone else will likely need to read your code long after you’re done with it.

The url( ) Function

In an effort to provide better flexibility in the long run, URL pattern tuples have been deprecated in favor of the url() utility function. url() takes the same arguments that are passed into the tuple, but can also take an extra keyword argument to specify the name of the URL pattern being described.

This way, a site can use the same view multiple times, yet still be able to be referenced using reverse URL lookups. More information on that can be found later in this section.

The include( ) Function

Rather than supplying all your URL patterns in a single file, the include() function allows them to be split up among multiple files. It takes a single argument: an import path where another URL configuration module can be found. This not only allows the URL configuration to be split across multiple files, but it also allows the regular expression to be used as a prefix for the included URL patterns.

One important thing to remember when using include() is to not specify the end of the string in the regular expression. The expression should never end in a dollar sign ($). The dollar sign ($) causes the expression to only match the full URL. This wouldn’t leave any additional URL fragments to pass along to the included configuration. This means that the extra URL patterns would only be matched if they check specifically for an empty string.

Resolving URLs to Views

Views are rarely called directly by your own code but are instead invoked by Django’s URL dispatch mechanism. This allows views to be decoupled from the particular URLs that trigger them, and the details of how those two aspects are linked can be safely ignored for most projects. But since views don’t always have to just be simple functions, knowing how Django goes from URL to view is important in order to determine what views are truly capable of.

Mapping URLs to views is a simple, well-documented process, but it’s worth covering the basics here for reference. A typical URL pattern consists of a few distinct items:

  • A regular expression to match against the incoming URL being requested
  • A reference to the view to be called
  • A dictionary of arguments to be passed along every time the view is accessed
  • A name to be used to reference the view during reverse lookups

Since URL patterns are expressed in regular expressions, which can capture certain portions of a string for later use, Django uses this as a natural way to pull arguments out of a URL so they can be passed to a view. There are two ways these groups can be specified, which determine how their captured values are passed into the view.

If groups are specified without names, they’re pulled into a tuple, which is passed along as excess positional arguments. This approach makes the regular expression a bit smaller, but it has some drawbacks. Not only does it make the regular expression a bit less readable, it also means that the order of arguments in your view must always match the order of the groups in the URL, because Django sends them in as positional arguments. This couples the URL to the view more than is usually preferable; in some situations, such as the object-based views described later in this chapter, it can still be quite useful.

If groups are given names, Django will create a dictionary mapping those names to the values that were extracted from the URL. This alternative helps encourage looser coupling between URLs and views by passing captured values to the view as keyword arguments. Note that Django doesn’t allow named and unnamed groups to be used together in the same pattern.

Resolving Views to URLs

As alluded to in the previous section, there’s another URL resolution process that Django provides, which can be of even more use if applied properly. Applications often need to provide links or redirects to other parts of the application or elsewhere on the site, but it’s not usually a good idea to hard-code those links directly. After all, even proprietary applications can change their URL structure, and distributed applications may not have any idea what the URL structure looks like in the first place.

In these situations, it’s important to keep the URLs out of the code. Django offers three distinct ways to specify a location without needing to know its URL in advance. Essentially, these all work the same way, as they all use the same internal machinery, but each interface is suited for a particular purpose.

The permalink Decorator

One of the most obvious places for code to reference a URL is in the get_absolute_url() method of most models. Providing this method is a common convention, so templates can easily provide a direct link to an object’s detail page without having to know or care what URL or view is used to display that page. It doesn’t take any arguments and returns a string containing the URL to be used.

To accommodate this situation, Django provides a decorator, living at django.db.models.permalink, which allows a function to return a set of values describing a view to be called, transforming it into a URL that calls the view. These values are provided as the return value from a function such as the get_absolute_url() method and follow a specific structure—a tuple containing up to three values.

  • The first value is the name of the view to be called. If the view was named, that name should be used here. If not, the import path of the view should be used instead. This is always required.
  • The second value is a tuple of positional arguments that should be applied to the view. If there are no arguments to be applied to the view at all, this value doesn’t need to be provided, but if keywords are needed, this should be an empty tuple.
  • The third value in this tuple is a dictionary mapping keyword arguments to their values, all of which will be passed to the specified view. If no keyword arguments are necessary, this value can be left out of the tuple.

Given the following URL configuration:

from django.conf.urls.defaults import *
from django.views.generic.detail import DetailView
from library.import models
 
class LibraryDetail(DetailView):
    queryset = models.Article.objects.all()
 
urlpatterns = patterns('django.views.generic',
    url(r'^articles/(?P<object_id>d+)/$', LibraryDetail.as_view(),
        name='library_article_detail'),
)

a corresponding model (located in a library application) might look like this:

from django.db import models
 
class Article(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField()
    pub_date = models.DateTimeField()
 
    def get_absolute_url(self):
        return ('library_article_detail',
            (), {'object_id': self.id})
    get_absolute_url = models.permalink(get_absolute_url)

The url Template Tag

Another common need is to have templates provide links to views that aren’t based on models but still shouldn’t have a hard-coded URL. For instance, a link to a contact form doesn’t necessarily have any ties to the database or any models, but will still need to be linked to in a way that can accommodate future changes or distribution.

The syntax for this template looks quite similar to the permalink decorator because it passes values to the same utility function. There are some slight differences, because as a template tag, it doesn’t use true Python code.

{% url library_article_detail object_id=article.id %}

The reverse( ) Utility Function

Django also provides a Python function that provides the translation from a description of a view and its arguments to a URL that will trigger the specified view. Living at django.core.urlresolvers, the reverse() function does exactly that. It takes all the same arguments described for the previous two techniques, but also one other, allowing it to specify which URL configuration module should be used to resolve the URL. This function is used internally by both the permalink decorator and the url template tag. The reverse() function takes up to four arguments.

  • viewname—The name of the view to be called or the import path if no name was specified. This is always required.
  • urlconf—The import path of a URL configuration module to use for lookups. This is optional and if it’s absent or None, the value is taken from the ROOT_URLCONF setting.
  • args—A tuple of any positional arguments that will be passed to the view.
  • kwargs—A dictionary of any keyword arguments that will be passed to the view.

Using the same example as in the previous section, here’s how reverse() would be used to obtain a URL for a specific object.

>>> from django.core.urlresolvers import reverse
>>> reverse('library_article_detail', kwargs={'object_id': 1})
'/articles/1/'

Keep in mind that args and kwargs are separate, distinct arguments. The reverse() utility function does not use any form of the argument expansion described in Chapter 2.

POSITIONAL VS. KEYWORD ARGUMENTS

To illustrate best practice, the examples in this section all use named groups in the URL’s regular expression, which allows—in fact, requires—the reverse resolution to specify arguments using keywords. This greatly improves the readability and maintainability of your code, which is a primary goal of writing Python. It is possible, though, to specify URLs without naming the capture groups, which requires reverse resolution to use positional arguments only.

For example, if the URL pattern was defined as r'^articles/(d+)/$', here’s how the previous examples would have to be written in order to work properly:

  • The permalink decorator—return ('library_article_detail', (self.id,), {})
  • The url template tag—{%url library_article_detail article.id %}
  • The reverse() function—reverse('library_article_detail', args=(1,))

Since a URL configuration only allows positional arguments or keyword arguments, but not both, there’s no need to specify both types together in the same reverse resolution call.

Function-Based Views

One point of confusion for programmers coming from other environments is the fact that Django uses the term “view” a bit differently than others. Traditionally, the view in a Model-View-Controller (MVC) architecture refers to the display of information to a user—essentially, the output portion of a user interface.

The Web doesn’t work like that. Viewing data is typically a direct result of a user action, and updates to that view only take place as responses to subsequent actions. This means that the output process is irrevocably linked to the user input process, which can cause some confusion about how even the traditional MVC pattern should define a view.

So there is no simple answer to the question of how Django’s views compare to those of other environments because there isn’t anything solid to compare against. People from different backgrounds are likely to have different expectations about what a view should be. The bad news is that Django probably doesn’t line up with any of them. The good news is that once you start working with Django, the notion of a view is clearly defined, so there’s little confusion when communicating with other Django developers.

Templates Break It Up a Bit

Django’s views do perform the basic function of the output interface, because they’re responsible for the response that is sent to the browser. In a strict sense, this response is the entire output, and it contains all the information about what the user will see. This is often too much work to do in Python while still making it readable, so most views rely on templates to generate the bulk of the content.

The most common practice is to have each view call a single template, which may make use of a number of tools to minimize the amount of template code that must be written for use by a particular view. Chapter 6 includes further details on the template language and the tools that can be used, but the important thing to know for this section is that templates are a great way to simplify the coding process as a whole. They help cut down on the amount of code that must be written, while simultaneously making that code more readable and maintainable for the future.

While Chapter 1 listed templates as a separate layer, remember that they’re really just a tool that Django makes available to other parts of an application, including views. Ultimately, whether or not templates are used to generate content, the view alone is responsible for generating the final response. Django’s template system has no concept of requests or responses; it just generates text. It’s up to views to handle the rest.

Anatomy of a View

A view is a function that takes an HTTP request and returns an HTTP response. That is a bit simplistic, given the potential power of views, but that’s really all there is to it. A view always receives, as its first argument, the HttpRequest created by Django, and it should always return an HttpResponse, unless something went wrong. Full details on those objects, their purpose and their properties are covered in Chapter 7.

The first aspect of that definition is the notion that a view must be a standard function. This definition is a bit flexible because in reality, any Python callable can be used as a view; it just happens that basic functions are easy to work with and provide everything that’s necessary for most situations. Methods—both on classes and instances—and callable objects, using the protocol described in Chapter 2, are all perfectly valid for use as views. This opens up a variety of other possibilities, some of which will be described later in this chapter.

The next point is the one immutable when it comes to views. Whenever a view is called, regardless of what other arguments are passed along, the first argument is always an HttpRequest object. This also means that all views must accept at least this one object, even those views that don’t have use for any explicit arguments. Some simple views, such as those that display the server’s current time, may not even use the request object, but must always accept it anyway to fulfill the basic protocol of a view.

On the subject of arguments, another point is that a view must be able to accept whatever arguments are passed to it, including those captured from the URL and those passed into the site’s URL configuration. This may seem obvious, but a common point of confusion is the presumption that Django uses some kind of magic to allow a URL configuration to specify which template should be used, without requiring any supporting code in the view.

Django’s generic views all allow you to specify the template name, and many users assume that Django somehow passes this straight through to the template system to override whatever name the view uses by default. The truth is that the generic views have special handling for this argument, and the view itself is responsible for telling the template system which template to use. Django relies on standard Python, so there’s no magic behind the scenes that tries to interpret what your arguments are supposed to mean. If you plan to supply an argument to a function, make sure that the view knows how to deal with it.

The last notion from that original description of views is that a view must return an HttpResponse object, and even that isn’t entirely accurate. Returning a response is definitely the primary goal of all views, but in certain situations it’s more appropriate to raise an exception, which will be handled in other ways.

What goes on between request and response is largely unrestricted, and views can be used for as many purposes as there are needs to be met. Views can be built to serve a specific purpose or they can be made generic enough to be used in distributed applications.

Writing Views to Be Generic

A common theme in Django development is to make code as reusable and configurable as possible so that applications and snippets are useful in more than one situation, without having to rewrite code for every need. That’s the whole point of DRY: Don’t Repeat Yourself.

Views present a bit of a challenge with regards to DRY, since they’re only called by incoming requests. It may seem like it wouldn’t be possible to write a view that could be called for anything other than the request it was originally intended for. Django itself, however, is full of examples of generic views, which can be used for a variety of applications and situations with only a small amount of configuration necessary for each new use.

There are a few guidelines that can greatly aid the reuse of views, making them generic enough to be used throughout a variety of applications. Views can even be made so generic that they can be distributed to others and included in projects the original author had no concept of.

Use Lots of Arguments

Typically, a view could perform quite a few different tasks, all combining to solve a particular problem. Each of these tasks often has to make assumptions about how it should work, but these assumptions can typically be pulled out into a configurable option using arguments. Consider the following view, designed to retrieve a blog post and pass it along to a template.

from django.shortcuts import render_to_response
from django.template import RequestContext
 
from blog.models import Post
 
def show_post(request, id):
    post = Post.objects.get(id=id)
    context = RequestContext(request, {'post': post})
    return render_to_response('blog/detail.html', context)

This view will work perfectly well for its intended purpose, but it’s quite tightly connected to a specific blog application. It’s still loosely coupled in the sense that it doesn’t need to deal with the details of how to retrieve the blog post or render the template, but still relies on details specific to the blog application, such as the model and template.

Instead, it’s possible to move these assumptions into arguments that can be swapped out for other situations. While initially this will involve some extra work, it can save a lot of time later, if this view is used in a great number of situations. More importantly, the more complex the view, the more code that can be reused using this technique. Once these options have been moved out into arguments, specific values can be passed in with a URL configuration, so a view doesn’t have to be written for each purpose.

For this particular view, a few things can be factored out in this way. The model doesn’t need to be known in advance and the view should also be able to work with a QuerySet so that a particular URL could operate on a limited set of data. Also, the field name shouldn’t be hard-coded, and the template name should be provided outside the view.

from django.shortcuts import render_to_response
from django.template import RequestContext
 
def show_object(request, id, model, template_name):
    object = model._default_manager.get(pk=id)
    context = RequestContext(request, {'object': object)})
    return render_to_response(template_name, context)

Then, when it comes time to use this view, it’s easy to customize by providing these details using a URL configuration. Simply supply the argument values as an extra dictionary in the URL configuration, and they’ll be passed along each time the view is called from that URL pattern.

from django.conf.urls.defaults import *
 
from blog.models import Post
 
urlpatterns = patterns('',
    (r'^post/(?P<id>d+)/$', 'blog.views.show_object', {
        'model': Post,
        'template_name': 'blog/detail.html',
    }),
)

This approach can even be used with models that use other types of IDs, such as a music database using catalog numbers in the format of DJNG-001; anything that can be guaranteed unique among all objects can be used as an object’s primary key. Since our new generic view simply passes the ID straight through to the database API, it’s easy to support these other types of IDs by simply adjusting the URL pattern appropriately.

r'^album/(?P<id>[a-z]+-[0-9])/$'

This particular view shouldn’t have to be written in the first place, because Django provides one out of the box for this purpose, DetailView, and it’s even more versatile than the example shown here. It uses nearly a dozen different arguments, all of which are expected to be customized in URL configurations.

Once you have a view that accepts a number of arguments for customization, it can become quite easy to require far too many arguments be specified in each URL configuration. If every use of a view requires all the configuration options to be specified, it could quickly become just as much work to use the generic view as it would be to write the view from scratch each time. Clearly, there needs to be a better way to manage all these arguments.

Provide Sensible Defaults

Since functions can define default values for any arguments that can use them, the most reasonable way to manage this complexity is to provide decent defaults wherever possible. Exactly what defaults can be provided and what they look like will be different for each view, but it’s usually possible to come up with some sensible values for them.

Sometimes you have a number of views that each serve a different purpose but may have some code in common. This is often boilerplate, which every view needs to use, but isn’t geared toward the true functionality of any individual view.

For example, views for private pages must always verify that users are logged in and that they have the appropriate permissions. An application may have a dozen different types of views, but if they’re all private, they must all use that same code every time. Thankfully, we’re working in Python, which provides a useful alternative.

View Decorators

Most boilerplate in views is either at the very beginning or the very end. Usually it handles such tasks as initializing various objects, testing standard prerequisites, handling errors gracefully or customizing the response before it goes out to the browser. The real meat of the view is what sits in the middle, and that’s the part that’s fun to write. Described in Chapter 2, decorators are a great way to wrap several functions in some common code that can be written once and tested easily, which reduces both bugs and programmer fatigue. Since views are typically just standard Python functions, decorators can be used here as well.

Chapter 2 illustrated how decorators can be used to write a wrapper around the original function, which can then access all the arguments that were intended for that function, as well as the return value from the function itself. In terms of views, this means that decorators always have access to the incoming request object and the outgoing response object. In some cases, a decorator can be special-cased for a particular application, which would allow it to anticipate a greater number of arguments that are specific to that application.

There are a number of things decorators can offer views, and a few of them are common enough to warrant inclusion in Django itself. Living at django.views.decorators are a few packages containing decorators you can use on any view in any application. The following packages are listed with just the trailing portion of their full import path provided, given that they all live at the same location.

  • cache.cache_page—Stores the output of the view into the server’s cache so that when similar requests come in later, the page doesn’t have to be re-created each time.
  • cache.never_cache—Prevents caching for a particular view. This is useful if you have site-wide caching set up but certain views can’t afford to go stale.
  • gzip.gzip_page—Compresses the output of the view and adds the appropriate HTTP headers so the Web browser knows how to handle it.
  • http.conditional_page—Only sends the whole page to the browser if it has changed since the last time the browser got a copy of it.
  • http.require_http_methods—Accepts a list of HTTP methods (described in detail in Chapter 7) that the view is limited to. If the view is called with any other method, it sends a response telling the browser it’s not allowed, without even calling the view. Two included shortcut variations are http.require_GET and http.require_POST, which don’t take any arguments and are hard coded for GET and POST requests, respectively.
  • vary.vary_on_header—Helps control browser-based caching of pages by indicating that the page’s content changes, depending on the values of the headers passed into the decorator. A simple variant specific to the Cookie header is available at vary.vary_on_cookie.

Additional decorators are provided as part of the bundled applications living at django.contrib. These decorators all live below that path, so as in the previous list, only the relevant path is supplied:

  • admin.views.decorators.staff_member_required—A simple decorator that checks the current user to see if it has staff access. This is used automatically for all the views in Django’s built-in admin, but could also be used for any other staff-only views on your site. If the user doesn’t have staff permissions, the decorator redirects the browser to the admin’s login page.
  • auth.decorators.user_passes_test—Accepts a single argument, which is a function to test the current user against some arbitrary condition. The provided function should accept just the User object and return True if the test passes or False if it fails. If the test passes, the user will be granted access to the page, but if it fails, the browser will redirect to the site’s login page, as determined by the LOGIN_URL setting.
  • auth.decorators.login_required—A specialized version of user_passes_test, this decorator simply checks that the user is logged in before allowing access to the view.
  • auth.decorators.permission_required—Another specialization of user_passes_test, this checks that the user has a given permission before the view is loaded. The decorator takes a single argument: the permission to be checked.

These are just the decorators that are bundled with Django itself. There are many other purposes for decorators, and third-party applications can provide their own as well. In order for these decorators to be of any use, however, they must be applied to views.

Applying View Decorators

Chapter 2 described how decorators can be applied to standard Python functions. Applying decorators to views works the same way, but there’s a notable difference: views aren’t always under your control.

The techniques described in Chapter 2 assume that the functions you decorate are your own. While that’s often the case, the number of distributed applications means that many Django-powered Web sites will use code from other sources, with views of their own. Applying decorators as described previously would require changes to the third-party code.

The goal is to apply decorators to third-party views without actually modifying third-party code. The key to doing this lies in the older-style decorator syntax from Python 2.3 and earlier. Remember that the new syntax allows decorators to be applied above the function definition, but the older syntax relies on passing the function to the decorator directly. Since Python functions can be imported from anywhere and can be passed in as arguments at any time, this is an excellent way to create decorated views from third-party code.

Also remember that the URL configuration is defined in a Python module, which gets executed when it is read. This makes the wide array of Python available to this configuration, including the ability to pass functions into decorators to create new functions.

from django.conf.urls.defaults import *
from django.contrib.auth.decorators import login_required
from thirdpartyapp.views import special_view
 
urlpatterns = patterns('',
    (r'^private/special/$', login_required(special_view)),
)

Writing a View Decorator

Chapter 2 covered how decorators themselves work and how they can be written to work in a variety of situations, though decorators for views have a few specific details that should be noted. These have less to do with the technical side of writing decorators and more with the nuances of how to achieve certain useful effects when working with views specifically.

The most common task decorators are used for with views is to create a wrapper function around the original view. This allows the decorator to perform extra work beyond what the view itself would ordinarily do, including

  • Performing additional work based on the incoming request or altering its attributes
  • Altering the arguments to be passed to the view
  • Modifying or replacing the outgoing response
  • Handling errors that occur inside the view
  • Branching to some other code, without even executing the view

The first thing to consider when writing a decorator is that it receives all the arguments intended for the view itself. Previous sections covered this, but only in the usual context of using *args and **kwargs to receive the arguments and pass them straight through to the wrapped function. With views, you know in advance that the first argument will always be the incoming request object, so a wrapper function can anticipate this and receive the request separately from the other arguments.

By interacting with the request object prior to executing the view, decorators can do two important things: make decisions based on the incoming request and make changes to the request to alter how the view operates. These tasks aren’t mutually exclusive and many decorators do both, such as the following example from Django.

from django.utils.functional import wraps
 
def set_test_cookie(view):
    """
    Automatically sets the test cookie on all anonymous users,
    so that they can be logged in more easily, without having
    to hit a separate login page.
    """
    def wrapper(request, *args, **kwargs):
        if request.user.is_anonymous():
            request.session.set_test_cookie()
        return view(request, *args, **kwargs)
    return wraps(view)(wrapper)

PRESERVING A VIEW’S NAME AND DOCUMENTATION

The built-in admin interface generates documentation for your application’s views using the name and docstring of the view function itself. By using decorators to wrap the function, we’re essentially replacing the original view function with the wrapper. This causes the admin interface to see the wrapper instead of the view.

Ordinarily, this would cause the name and docstring of the view to be lost in the shuffle, so the admin’s documentation feature doesn’t work properly with these views. To get the right documentation, those attributes of the function must remain intact throughout the wrapping process.

Django provides an additional decorator, living at django.utils.functional.wraps, which is designed to copy these attributes onto the wrapped function so it looks more like the original view. This process is described in more detail in Chapter 9, but all the examples in this section use it to illustrate best practices for decorating views.

Another common use of decorators is to extract some common code from the beginning or end of a set of views. This can be especially useful when looking at incoming arguments, as decorators can perform any lookups and initializations prior to calling the view. Then, decorators can simply pass fully prepared objects to the view, rather than raw strings captured from a URL.

from django.utils.functional import wraps
from django.shortcuts import get_object_or_404
 
from news.models import Article
 
def get_article_from_id(view):
    """
    Retrieves a specific article, passing it to the view directly
    """
    def wrapper(request, id, *args, **kwargs):
        article = get_object_or_404(Article, id=int(id))
        return view(request, article=article, *args, **kwargs)
    return wraps(view)(wrapper)

The great thing about a decorator like this is that, even though the logic it contains is fairly minimal, it does cut down on the amount of code that has to be duplicated for views that all get an Article object according to an ID provided in the URL. This not only makes the views themselves a bit more readable, but any time you can cut down on code that has to be written, you can help reduce bugs.

Also, by having access to the response, decorators can make some interesting decisions about how that response should behave. Middleware classes, described in Chapter 7, have much more use for accessing the response, but there are still useful things decorators can do.

Of note is the ability to set the content-type of the response, which can control how the browser deals with the content once it receives it. Chapter 7 describes this in more detail and also how it can be set when creating the response. However, it’s also possible to set it after the response has already been created and returned from a view.

This technique can be a good way to override the content-type for specific types of views. After all, if no content-type is specified, Django pulls a value from the DEFAULT_CONTENT_TYPE setting, which defaults to 'text/html'. For certain types of views, especially those intended for Web services, it may be better to serve them using another content-type, such as 'application/xml', while still being able to use generic views.

from django.utils.functional import wraps
 
def content_type(c_type):
    """
    Overrides the Content-Type provided by the view.
    Accepts a single argument, the new Content-Type
    value to be written to the outgoing response.
    """
    def decorator(view):
        def wrapper(request, *args, **kwargs):
            response = view(request, *args, **kwargs)
            response['Content-Type'] = c_type
            return response
        return wraps(view)(wrapper)
    return decorator

This decorator could then accept a content-type when applying it to a view.

@content_type('application/json')
def view(request):
    ...

A lesser-used feature of view decorators is the ability to catch any exceptions that are raised by the view or any code it executes. Views typically just return a response directly, but there are still many situations where a view may opt to raise an exception instead. One common example, found in many of Django’s own generic views, is raising the Http404 exception to indicate that an object couldn’t be found.

Chapter 9 covers the exceptions Django provides in its standard distribution, many of which can be raised by views for one reason or another. In addition, many of the standard Python exceptions could be raised for various situations, and it can be useful to catch any of these. A decorator can perform a variety of additional tasks when an exception is raised, from simply logging the exception to the database to returning a different type of response in the case of certain exceptions.

Consider a custom logging application with a log entry model like this:

from datetime import datetime
 
from django.db import models
 
class Entry(models.Model):
    path = models.CharField(max_length=255)
    type = models.CharField(max_length=255, db_index=True)
    date = models.DateTimeField(default=datetime.utcnow, db_index=True)
    description = models.TextField()

The application providing this model could also provide a decorator for projects to apply to their own views that logs exceptions to this model automatically.

from django.utils.functional import wraps
 
from mylogapp.models import Entry
 
def logged(view):
    """
    Logs any errors that occurred during the view
    in a special model design for app-specific errors
    """
    def wrapper(request, *args, **kwargs):
        try:
            return view(request, *args, **kwargs)
        except Exception as e:
            # Log the entry using the application’s Entry model             Entry.objects.create(path=request.path,
                                 type='View exception',
                                 description=str(e))
 
            # Re-raise it so standard error handling still applies
            raise
    return wraps(view)(wrapper)

The recurring theme with all these examples is that view decorators can encapsulate some common code that would otherwise have to be duplicated in every instance of the view. In essence, view decorators are a way to extend the view’s code before or after the original code. It’s important to generalize these examples in order to realize just how much is possible with view decorators. Any boilerplate you find yourself duplicating at the beginning or end of your views is fair game to be placed in a decorator to save some time, energy and trouble.

Class-Based Views

Views don’t have to be limited to functions. In the end, all that matters to Django is that it gets a callable; how that callable is created is still up to you. You can also define your views as classes, which provides a few key advantages over traditional functions.

  • Higher degree of configurability
  • Easier customization for specialized applications
  • Reuse of objects that may be used for other purposes

Even though the end result must be a callable, there are even more ways to create classes than there are for functions. Django’s own generic views follow a particular structure though, and in the spirit of maintaining similarity where possible, it’s a good idea to try to match up with that. Just remember that you can write your classes differently if this format doesn’t suit your needs as easily as something else.

django.views.generic.base.View

The easiest way to get the basics into your class is to subclass Django’s own View class. It won’t do everything you’ll need out of the box, of course, but it provides the basics:

  • Validates arguments passed into the view configuration
  • Prevents using arguments named after HTTP methods
  • Collects arguments passed in the URL configuration
  • Keeps request information in a convenient place for methods to access
  • Verifies that a requested HTTP method is supported by the view
  • Automatically handles OPTIONS requests
  • Dispatches to view methods based on the requested HTTP method

Some of this functionality is specific to it being a class, such as making convenient request information conveniently available to various view methods. Others, such as enforcing specific HTTP methods and handling OPTIONS requests directly, are really just good HTTP practices that are made easier by the fact that a class can provide such functionality without you having to remember to defer to it in your own code.

Classes offer a chance for multiple methods to interact, so Django uses a few standard methods to do the common items, while providing you a way to add in other methods that now only need to worry about the specifics of your application. And since it’s just a class, you can also add other methods as you see fit. Django won’t do anything with them, so you’ll have to call them yourself, but it does give you a chance to abstract out common code more easily than if you were working with raw functions.

All of Django’s generic views inherit from a common ancestor to provide all these hooks, and it’s easy for yours to do the same. Let’s take a look at some of the methods Django provides on a default generic view.

__init__(self, **kwargs)

As a class designed to create objects, __init__() is obviously where instances of the class start out. Its keyword arguments are options defined in the URL, but you don’t actually call this directly at any time. Instead, your URL configuration will use the as_view(), which does a few things, including initializing the class.

The default implementation of __init__() simply sets all supplied keyword arguments as instance variables on the view object.

BE CAREFUL OVERRIDING __INIT__()

As you’ll see in the following sections, the view class doesn’t get instantiated until a request arrives and is sent to the generated view function. This means that __init__() fires for every incoming request, rather than just once at the time Django processes your URL configuration.

Therefore, if you need to perform any changes to the configuration options or react to them in any way that doesn’t require access to any information from an actual request, you’ll want to override as_view() and add your logic there instead. In fact, __init__() doesn’t even see the request object itself; it only receives the arguments that were captured from the URL.

So while __init__() is typically a good place to provide additional configuration features, in this case it tends not to work out so well. It’s best to override as_view() if you need to work with the configuration and dispatch() if you need to work with anything related to an incoming request.

as_view(cls, **initkwargs)

This class method is the primary entry point into your view. When you configure a URL to use this view, you’ll call this method, which will return a view function for Django to use. You’ll also pass the configuration options into the method call, rather than placing them in a dictionary alongside the view itself. For example:

from django.views.generic.base import View 
 
urlpatterns = patterns('',
    (r'^example/', View.as_view(template_name='example.html')),
)

When called, as_view() is responsible for a few things:

  • It verifies that none of the provided options match the names of HTTP methods. If any are found, it raises a TypeError immediately, rather than waiting for a request to come in.
  • It also verifies that all of the provided options match existing named attributes on the class. This enforces a pattern where default options are established as class attributes, then overridden by individual URL configurations, as necessary. For instance, the preceding example would raise a TypeError because template_name isn’t named as an attribute on the built-in View class.
  • Then it creates a simple view function that will be returned to the URL configuration, for use when an actual request comes in. This view then gets updated with a few attributes from the class and any applied decorators, to make it more useful when introspecting it later.
  • Lastly, it returns the newly-created view function, so Django has something to work with when requests start coming in.

The view function created by as_view() is even simpler still. It accepts a request, as well as *args and **kwargs, so it can receive anything captured from the URL, according to the regular expression in the URL configuration. This is identical to how any other view would work; there is nothing special about how Django’s URL dispatching handles a class-based view.

Once it has this information, it’s only responsible for a little bit of record-keeping and a call to something more useful:

  • First, it creates an instance of the view class, passing in the configuration options supplied to as_view(). This is where __init__() finally comes into play, because the instance of a view is only good for a single request. Each subsequent request will get a new instance of the view class.
  • Next, it checks to see if the view has get() and head() methods. If it has get() but not head(), it sets the view up so that HEAD requests will get sent to the get() method. In general, HEAD should act just like GET but without returning content, so this is a reasonable default behavior.
  • Then, it sets the request and URL-captured information onto the object, as instance attributes named request, args and kwargs. You may not need to access this information as attributes on the object, but they’re there if you do need them.
  • Lastly, it defers execution to the dispatch() method, passing in the request and all captured arguments, exactly as they were passed into the view itself.

dispatch(self, request, *args, **kwargs)

This is the point where requests are properly handled. Like any view, it’s responsible for accepting a request and returning a response. Its default implementation handles some of the complexities of different HTTP methods, while allowing you to just write your code in additional view methods.

  • It first checks to see if the requested HTTP method is valid and has a matching view method on the class to handle the request. If not, it returns a response with a status code of 405 Method Not Allowed, rather than even trying to serve it in any additional capacity.
  • If the class does have a matching view method, dispatch() simply defers to that, passing all the arguments into it.

The first test for HTTP methods checks a lower-cased copy of the method string against a list of known methods, stored as a class attribute called http_method_names:

  • get
  • post
  • put
  • delete
  • head
  • options
  • trace

Notice that newer options, such as PATCH, aren’t present in this list. If you do have need for a different method and are hosting in an environment that will pass it through to Django, you can override this list to add any additional methods you need. If an HTTP method comes in that’s not in this list, Django won’t allow it, even if you have a view method of the same name.Django’s behavior here is generally preferred, but you can override dispatch() to provide other functionality if you’d like. For example, if you have an API that can return data in various formats, you might use the dispatch() method to format the output as needed, leaving the individual methods to just retrieve and return the raw data.

Individual View Methods

After dispatch() determines that the view can handle the request, it sends that request off to one of several possible functions, named according to HTTP methods. For example, a GET request will get routed off to get(), POST requests go to post(), and so on. Each of these would behave just like a standard view function, accepting a request and additional arguments and returning a response.

To demonstrate how this helps your code in practice, consider the following example of form processing in traditional function-based views:

def view(request, template_name='form.html'):
    if request.method == 'POST':
        form = ExampleForm(request.POST)
        if form.is_valid():
            # Process the form here
            return redirect('success')
        else:
            return render(request, template_name, {'form': form})
    else:
        form = ExampleForm()  # no data gets passed in
        return render(request, template_name, {'form': form})

This view services both GET and POST requests, so it has to deal with requests that form data that needs to be processed, while also managing requests without any data, to present the form in the first place. Here’s how that view would look with a class-based view instead.

class FormView(View):
    template_name = 'form.html'
 
    def get(self, request):
        form = ExampleForm()
        return render(request, self.template_name, {'form': form})
 
    def post(self, request):
        form = ExampleForm(request.POST)
        if form.is_valid():
            # Process the form here
            return redirect('success')
        else:
            return render(request, self.template_name, {'form': form})

This is a much cleaner separation of concerns, and as an added bonus, the class-based version will automatically handle HEAD and OPTIONS requests properly, while rejecting requests for PUT, DELETE and TRACE.

Django also provides a simple options() method, which indicates what features the URL can provide. The default behavior uses the available view methods to indicate which HTTP methods are allowed and provide those in the Allow header of the response. If you have more features that need to be included here, such as those necessary for cross-origin resource sharing,2 you can simply override options() to provide that information.

Decorating View Methods

The structure of these class-based views makes them somewhat interesting when it comes to decorators. On one hand, they’re classes, which can’t be decorated using the same decorators that are used for functions. In fact, classes can’t be decorated at all prior to Python 3. On the other hand, the as_view() method returns a simple function, which can be decorated just like any other.

The simplest technique to explain is decorating the output of as_view(). Because it returns a function, it can be decorated just like any other function. So if you need to require a user to be logged in, you can simply use the standard login_required decorator, as always.

from django.contrib.auth.decorators import login_required
 
urlpatterns = patterns('',
    (r'^example/', login_required( FormView.as_view(template_name='example.html'))) ,
)

On the other hand, you can decorate individual methods on your class directly, if you know of things that they’ll always need. Two things complicate matters more than the typical function case. First, these are instance methods, rather than simple functions, which means they accept a self argument, which isn’t there on traditional function-based views. As is often the case with problems regarding decorators, the solution is another decorator, which is in this case provided by Django itself. The method_decorator can be used to wrap a normal decorator in a way that lets it ignore self and just deal with the argument it expects.

from django.utils.decorators import method_decorator
 
class FormView(View):
    @method_decorator(login_required)
    def get(request):
        # View code continues here

The second issue is that there are now multiple functions involved, rather than just one that can be decorated directly. You can decorate any of the functions you’d like, but an interesting fact of the dispatching process for class-based views is that dispatch() is the only method that serves the same purpose as a traditional function. All requests go through it, and it also has access to all available information about the class, the instance and the incoming request.

Therefore, this is also the best place to apply any view decorators. If you apply a decorator to dispatch(), it’ll modify the behavior of every request, regardless of what other methods are used afterward. You can decorate individual methods if you have a good reason to, but it’s most useful to use dispatch() and have it work the same way it does in traditional function-based views.

Using an Object As a View

As described in Chapter 2, Python provides a way to define a class in such a way that instances of it can be called as if they were functions. If defined on a class, the __call__() method will be called when the object is passed in where a function is expected. As with any other callable, such objects can also be used as Django views.

There are as many ways to use objects as views as there are ways to define objects themselves. Aside from using __call__() to receive each incoming request, what happens inside the object is up for grabs. In a typical situation, the request would be dispatched to individual methods, similar to Django’s own class-based views, but you can do whatever you need.

Applied Techniques

By allowing custom objects and decorators to be used with URL patterns and views, nearly any valid Python code can customize how a URL is mapped to a view and how the view itself is executed. The following is just a taste of what’s possible; the rest depends on the needs of your application.

Cross-Origin Resource Sharing (CORS)

Requests that cross from one domain to another are a security risk, because they could expose sensitive data to sites that shouldn’t have access to that data. Imagine if a random blog could just make an AJAX call to your bank’s website. If you were logged in and didn’t have any protection in your browser, that call could send your bank account information to a blog you know nothing about and just happened to visit.

Thankfully, modern browsers do have protection against this sort of thing. By default, requests made from one site to another within your browser will be forbidden. There are legitimate uses for cross-origin requests like that, though, such as between trusted sites or when serving public data files for general use. The Cross-Origin Resource Sharing (CORS) specification allows one site to indicate which other sites can access certain resources.

CORS Decorator

With a traditional function-based view, this functionality can be added as a decorator. Here’s how you could decorate a view to make it publicly available from any site that requests it:

@cross_origin(allow_origin=['*'])
def public_data(request):
    # Data retrieval goes here

The implementation is pretty straightforward, as far as decorators go:

def cross_origin(allow_credentials=False, allow_headers=None,
                 allow_methods=None, allow_headers=None,
                 allow_origin=None, expose_headers=None, max_age=None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(request, *args, **kwargs):
            headers = {}
 
            if access_control_allow_credentials:
                headers['Allow-Credentials'] = allow_credentials
            if access_control_allow_headers:
                headers['Allow-Headers'] = ', '.join(allow_headers)
            if access_control_allow_methods:
                headers['Allow-Methods'] = ', '.join(allow_methods)
            if access_control_allow_origin:
                headers['Allow-Origin'] = ' '.join(allow_origin)
            if access_control_expose_headers:
                headers['Expose-Headers'] = ', '.join(expose_headers)
            if access_control_max_age:
                headers['Max-Age'] = self.max_age
 
            response = func(request, *args, **kwargs)
 
            for name, value in headers:
                response.headers['Access-Control-%s' % name] = value
 
            return response
        return wrapper
    return decorator

There’s no need to support using the decorator without arguments, because if you don’t supply any arguments, it wouldn’t do anything anyway. So it only supports arguments and simply has to add all the right headers when the response comes back from the decorated view. As you can see, some of them accept lists, while others accept just a single value.

CORS Mixin

This decorator could be applied to a class-based view directly, using method_decorator, but to make it a bit easier to configure, we could instead use a mixin. Here’s how it would look on a similarly featured class-based view:

class PublicData(View, CrossOrigin): 
    access_control_allow_origin = ['*']
 
    def get(self, request):
        # Data retrieval goes here

The implementation gets a little more complicated than a simple decorator, but it’s still pretty simple to work with:

class CrossOrigin(object):
    """
    A view mixin that provides basic functionality necessary to add the necessary
    headers for Cross-Origin Resource Sharing
    """
 
    access_control_allow_credentials = False
    access_control_allow_headers = None
    access_control_allow_methods = None
    access_control_allow_origin = None
    access_control_expose_headers = None
    access_control_max_age = None
 
    def get_access_control_headers(self, request):
        headers = {}
 
        if self.access_control_allow_credentials:
            headers['Allow-Credentials'] = self.access_control_allow_credentials
        if self.access_control_allow_headers:
            headers['Allow-Headers'] = ', '.join(self.access_control_allow_headers)
        if self.access_control_allow_methods:
            headers['Allow-Methods'] = ', '.join(self.access_control_allow_methods)
        if self.access_control_allow_origin:
            headers['Allow-Origin'] = ' '.join(self.access_control_allow_origin)
        if self.access_control_expose_headers:
            headers['Expose-Headers'] = ', '.join(self.access_control_expose_headers)
        if self.access_control_max_age:
            headers['Max-Age'] = self.access_control_max_age
 
        return headers
 
    def dispatch(self, request, *args, **kwargs):
        response = super(CORSMixin, self).dispatch(request, *args, **kwargs)
 
        for name, value in self.get_access_control_headers(request):
            response.headers['Access-Control-%s' % name)] = value
 
        return response

Worth noting here is that the header functionality has been moved out into a separate method, which receives the request as an argument. This allows you to override that method in your subclass, in case you need to make changes to the CORS headers based on details of the incoming request.

For example, if you had a lot of different domains that need access to the resource, you could check the incoming request against those domains and only add that domain as an allowed origin, rather than having to include the entire list in every response. This is a prime example of how classes enable greater customization of internal details than a decorator, which tends to hide those implementations in a way that you can’t modify.

Providing Both a Decorator and a Mixin

If you wanted to supply this as a reusable helper, you might even want to supply both the function decorator and the class mixin. This is easy to do by just pulling the common code out into a separate function, which can be called from each of the different approaches.

def cors_headers(allow_credentials=false, allow_headers=None, allow_methods=None,                 allow_origin=None, expose_headers=None, max_age=None):
    headers = {}
 
    if allow_credentials:
        headers['Access-Control-Allow-Credentials'] = allow_credentials
    if allow_headers:
        headers['Access-Control-Allow-Headers'] = ', '.join(allow_headers)
    if allow_methods:
        headers['Access-Control-Allow-Methods'] = ', '.join(allow_methods)
    if allow_origin:
        headers['Access-Control-Allow-Origin'] = ' '.join(allow_origin)
    if expose_headers:
        headers['Access-Control-Expose-Headers'] = ', '.join(expose_headers)
    if max_age:
        headers['Access-Control-Max-Age'] = self.max_age
 
    return response
 
def cross_origin(allow_credentials=false, allow_headers=None, allow_methods=None,
                 allow_origin=None, expose_headers=None, max_age=None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(request, *args, **kwargs):
            response = func(request, *args, **kwargs)
            headers = cors_headers(response, allow_credentials, allow_headers,
                                   allow_methods, allow_origin, expose_headers, max_age)
            response.headers.update(headers)
            return response
        return wrapper
    return decorator
 
class CrossOrigin(object):
    """
    A view mixin that provides basic functionality necessary to add the necessary
    headers for Cross-Origin Resource Sharing
    """
 
    access_control_allow_credentials = false
    access_control_allow_headers = None
    access_control_allow_methods = None
    access_control_allow_origin = None
    access_control_expose_headers = None
    access_control_max_age = None
 
    def get_access_control_headers(self, request):
        return cors_headers(self.access_control_allow_credentials,
                            self.access_control_allow_headers,
                            self.access_control_allow_methods,
                            self.access_control_allow_origin,
                            self.access_control_expose_headers,
                            self.access_control_max_age):
 
    def dispatch(self, request, *args, **kwargs):
        response = super(CORSMixin, self).dispatch(request, *args, **kwargs)
        headers = self.get_access_control_headers(request)
        response.headers.update(headers)
        return response

Now the only thing that the decorator and mixin have to do is collect arguments appropriately for each technique, leaving the details of applying the actual headers to a common function. This isn’t a groundbreaking technique, but it’s useful to see how decorators and mixins aren’t that different after all. They’re configured a bit differently, but in the end it still comes down to accepting a request and returning a response.

Now What?

URLs form the foundation of your site’s architecture, defining how users access the content and services you provide. Django stays out of the way when it comes to designing your URL scheme, so you’re free to build it however you like. Be sure to take the appropriate time and remember that URL configuration is still a form of site design.

Views are the real workhorses of any application, taking user input and turning it into useful output. While the whole of Python is available for views to use, Django does provide one very important tool to handle one of the most common user input tasks on the Web: forms.

1 http://prodjango.com/cool-uris-dont-change/

2 http://prodjango.com/cors/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset