One of the many strengths of Python is its “batteries included” philosophy; when you install Python, it comes with a large “standard library” of commonly used modules that you can start using immediately, without having to download anything else. Django aims to follow this philosophy, as it includes its own standard library of add-ons useful for common Web development tasks. This chapter covers that collection of add-ons.
Django’s standard library lives in the package django.contrib. Within, each subpackage is a separate piece of add-on functionality. These pieces are not necessarily related, but some django.contrib subpackages may require other ones.
There’s no hard requirement for the types of functionality in django.contrib. Some of the packages include models (and, hence, require you to install their database tables into your database), but others consist solely of middleware or template tags.
The single characteristic the django.contrib packages have in common is this: If you were to remove the django.contrib package entirely, you could still use Django’s fundamentals with no problems. When the developers of Django add new functionality to the framework, they use this rule of thumb in deciding whether the new functionality should live in django.contrib or elsewhere.
django.contrib consists of these packages:
The rest of this chapter goes into detail about each django.contrib package that hasn’t yet been covered in this book.
Django’s “sites” system is a generic framework that lets you operate multiple Web sites off of the same database and Django project. As this is an abstract concept, it can be tricky to understand — so we’ll start with a couple of examples.
As we explained in Chapter 1, the Django-powered sites LJWorld.com and Lawrence.com are operated by the same news organization — the Lawrence Journal-World newspaper in Lawrence, Kansas. LJWorld.com focuses on news, while Lawrence.com focuses on local entertainment. But sometimes editors want to publish an article on both sites.
The brain-dead way of solving the problem would be to use a separate database for each site, and to require site producers to publish the same story twice: once for LJWorld.com and again for Lawrence.com. But that’s inefficient for site producers, and it’s redundant to store multiple copies of the same story in the database.
The better solution is simple: Both sites use the same article database, and an article is associated with one or more sites via a many-to-many relationship. The Django sites framework provides the database table to which articles can be related. It’s a hook for associating data with one or more “sites.”
LJWorld.com and Lawrence.com both have e-mail alert functionality, which lets readers sign up to get notifications when news happens. It’s pretty basic: A reader signs up on a Web form, and he immediately gets an e-mail saying, “Thanks for your subscription.”
It’d be inefficient and redundant to implement this signup-processing code twice, so the sites use the same code behind the scenes. But the “thank you for signing up” notice needs to be different for each site. By using Site objects, we can abstract the “thank you” notice to use the values of the current site’s name (e.g., 'LJWorld.com') and domain (e.g., 'www.ljworld.com').
The Django sites framework provides a place for you to store the name and domain for each site in your Django project, which means you can reuse those values in a generic way.
The sites framework is more of a series of conventions than a framework. The whole thing is based on two simple concepts:
How you use these two concepts is up to you, but Django uses them in a couple of ways automatically via simple conventions.
To install the sites app, follow these steps:
To reuse data on multiple sites, as explained in “Example 1,” just create a ManyToManyField to Site in your models. For example:
from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
sites = models.ManyToManyField(Site)
That’s the necessary infrastructure you need in order to associate articles with multiple sites in your database. With that in place, you can reuse the same Django view code for multiple sites. Continuing the Article example, here’s what an article_detail view might look like:
from django.conf import settings
def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID)
except Article.DoesNotExist:
raise Http404
# ...
This view function is reusable because it checks the article’s site dynamically, according to the value of the SITE_ID setting.
For example, say LJWorld.com’s settings file has a SITE_ID set to 1 and Lawrence.com’s settings file has a SITE_ID set to 2. If this view is called when LJWorld.com’s settings file is active, then it will limit the article lookup to articles in which the list of sites includes LJWorld.com.
Similarly, you can associate a model to the Site model in a many-to-one relationship, using ForeignKey.
For example, if an article is only allowed on a single site, you’d use a model like this:
from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(maxlength=200)
# ...
site = models.ForeignKey(Site)
This has the same benefits as described in the last section.
On a lower level, you can use the sites framework in your Django views to do particular things based on what site in which the view is being called. For example:
from django.conf import settings
def my_view(request):
if settings.SITE_ID == 3:
# Do something.
else:
# Do something else.
Of course, it’s ugly to hard-code the site IDs like that. This sort of hard-coding is best for hackish fixes that you need done quickly. A slightly cleaner way of accomplishing the same thing is to check the current site’s domain:
from django.conf import settings
from django.contrib.sites.models import Site
def my_view(request):
current_site = Site.objects.get(id=settings.SITE_ID)
if current_site.domain == 'foo.com':
# Do something
else:
# Do something else.
The idiom of retrieving the Site object for the value of settings.SITE_ID is quite common, so the Site model’s manager (Site.objects) has a get_current() method. This example is equivalent to the previous one:
from django.contrib.sites.models import Site
def my_view(request):
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Do something
else:
# Do something else.
Note that in this final example, you don’t have to import django.conf.settings.
For a DRY (Don’t Repeat Yourself) approach to storing your site’s name and domain name, as explained in “Example 2,” just reference the name and domain of the current Site object. For example:
from django.contrib.sites.models import Site
from django.core.mail import send_mail
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
current_site = Site.objects.get_current()
send_mail('Thanks for subscribing to %s alerts' % current_site.name,
'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % current_site.name,
'editor@%s' % current_site.domain,
[user_email])
# ...
Continuing our ongoing example of LJWorld.com and Lawrence.com: On Lawrence.com, this e-mail has the subject line “Thanks for subscribing to lawrence.com alerts.” On LJWorld.com, the e-mail has the subject “Thanks for subscribing to LJWorld.com alerts.” This same site-specific behavior is done in the e-mail’s message body.
Note that an even more flexible (but more heavyweight) way of doing this would be to use Django’s template system. Assuming Lawrence.com and LJWorld.com have different template directories (TEMPLATE_DIRS), you could simply delegate to the template system like so:
from django.core.mail import send_mail
from django.template import loader, Context
def register_for_newsletter(request):
# Check form values, etc., and subscribe the user.
# ...
subject = loader.get_template('alerts/subject.txt').render(Context({}))
message = loader.get_template('alerts/message.txt').render(Context({}))
send_mail(subject, message, 'do-not-reply@example.com', [user_email])
# ...
In this case, you’d have to create subject.txt and message.txt templates in both the LJWorld.com and Lawrence.com template directories. That gives you more flexibility, but it’s also more complex.
It’s a good idea to exploit the Site objects as much as possible, to remove unneeded complexity and redundancy.
Django’s get_absolute_url() convention is nice for getting your objects’ URL without the domain name, but in some cases you might want to display the full URL — with http:// and the domain and everything — for an object. To do this, you can use the sites framework. A simple example:
>>> from django.contrib.sites.models import Site >>> obj = MyModel.objects.get(id=3) >>> obj.get_absolute_url() '/mymodel/objects/3/' >>> Site.objects.get_current().domain 'example.com' >>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url()) 'http://example.com/mymodel/objects/3/'
If Sites play a key role in your application, consider using the helpful CurrentSiteManager in your model(s). It’s a model manager (see Chapter 5) that automatically filters its queries to include only objects associated with the current Site.
Use CurrentSiteManager by adding it to your model explicitly. For example:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(maxlength=100)
pub_date = models.DateField()
site = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager()
With this model, Photo.objects.all() will return all Photo objects in the database, but Photo.on_site.all() will return only the Photo objects associated with the current site, according to the SITE_ID setting.
In other words, these two statements are equivalent:
Photo.objects.filter(site=settings.SITE_ID) Photo.on_site.all()
How did CurrentSiteManager know which field of Photo was the Site? It defaults to looking for a field called site. If your model has a ForeignKey or ManyToManyField called something other than site, you need to explicitly pass that as the parameter to CurrentSiteManager. The following model, which has a field called publish_on, demonstrates this:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Photo(models.Model):
photo = models.FileField(upload_to='/home/photos')
photographer_name = models.CharField(maxlength=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')
If you attempt to use CurrentSiteManager and pass a field name that doesn’t exist, Django will raise a ValueError.
Finally, note that you’ll probably want to keep a normal (non-site-specific) Manager on your model, even if you use CurrentSiteManager. As explained in Chapter 5, if you define a manager manually, then Django won’t create the automatic objects = models.Manager() manager for you. Also, note that certain parts of Django — namely, the Django admin site and generic views — use whichever manager is defined first in the model, so if you want your admin site to have access to all objects (not just site-specific ones), put objects = models.Manager() in your model, before you define CurrentSiteManager.
Although it’s not required that you use the sites framework, it’s strongly encouraged, because Django takes advantage of it in a few places. Even if your Django installation is powering only a single site, you should take the two seconds to create the site object with your domain and name, and point to its ID in your SITE_ID setting.
Here’s how Django uses the sites framework:
Often times, you’ll have a database-driven Web application up and running, but you’ll need to add a couple “one-off” static pages, such as an “About” page or a “Privacy Policy” page. It’d be possible to use a standard Web server such as Apache to serve these files as flat HTML files, but that introduces an extra level of complexity into your application, because then you have to worry about configuring Apache, you’ve got to set up access for your team to edit those files, and you can’t take advantage of Django’s template system to style the pages.
The solution to this problem is Django’s “flatpages” app, which lives in the package django.contrib.flatpages. This app lets you manage such “one-off” pages via Django’s admin site, and it lets you specify templates for them using Django’s template system. It uses Django models behind the scenes, which means it stores the pages in a database, just like the rest of your data, and you can access flatpages with the standard Django database API.
Flatpages are keyed by their URL and site. When you create a flatpage, you specify which URL it’s associated with, along with which site(s) it’s on. (For more on sites, see the “Sites” section above.)
To install the flatpages app, follow these steps:
The flatpages app creates two tables in your database: django_flatpage and django_flatpage_sites. django_flatpage is a lookup table that simply maps a URL to a title and bunch of text content. django_flatpage_sites is a many-to-many table that associates a flatpage with one or more sites.
The app comes with a single FlatPage model, defined in django/contrib/flatpages/models.py. It looks like this:
from django.db import models
from django.contrib.sites.models import Site
class FlatPage(models.Model):
url = models.CharField(maxlength=100)
title = models.CharField(maxlength=200)
content = models.TextField()
enable_comments = models.BooleanField()
template_name = models.CharField(maxlength=70, blank=True)
registration_required = models.BooleanField()
sites = models.ManyToManyField(Site)
Let’s cover these fields one at a time:
You can create flatpages through either the Django admin interface or the Django database API. For more, see “How to add, change and delete flatpages” below.
Once you’ve created flatpages, the FlatpageFallbackMiddleware does all of the work. Each time any Django application raises a 404 error, this middleware checks the flatpages database for the requested URL as a last resort. Specifically, it checks for a flatpage with the given URL with a site ID that corresponds to the SITE_ID setting.
If it finds a match, it loads the flatpage’s template, or flatpages/default.html if the flatpage has not specified a custom template. It passes that template a single context variable, flatpage, which is the flatpage object. It uses RequestContext in rendering the template.
If it doesn’t find a match, the request continues to be processed as usual.
Note that this middleware only gets activated for 404s — not for 500s or responses of any other status code. Also note that the order of MIDDLEWARE_CLASSES matters. Generally, you can put FlatpageFallbackMiddleware at the end of the list, because it’s a last resort.
If you’ve activated the automatic Django admin interface, you should see a “Flatpages” section on the admin index page. Edit flatpages as you edit any other object in the system.
As described above, flatpages are represented by a standard Django model that lives in django/contrib/flatpages/models.py. Hence, you can access flatpage objects via the Django database API. For example:
>>> from django.contrib.flatpages.models import FlatPage >>> from django.contrib.sites.models import Site >>> fp = FlatPage( ... url='/about/', ... title='About', ... content='<p>About this site...</p>', ... enable_comments=False, ... template_name='', ... registration_required=False, ... ) >>> fp.save() >>> fp.sites.add(Site.objects.get(id=1)) >>> FlatPage.objects.get(url='/about/') <FlatPage: /about/ -- About>
By default, flatpages are rendered via the template flatpages/default.html, but you can override that for a particular flatpage.
Creating the flatpages/default.html template is your responsibility. In your template directory, just create a flatpages directory containing a file default.html.
Flatpage templates are passed a single context variable, flatpage, which is the flatpage object.
Here’s a sample flatpages/default.html template:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>{{ flatpage.title }}</title>
</head>
<body>
{{ flatpage.content }}
</body>
</html>
Django’s redirects framework lets you manage redirects easily by storing them in a database and treating them as any other Django model object. For example, you can use the redirects framework to tell Django, “redirect any request to /music/ to /sections/arts/music/.”
To install the redirects app, follow these steps:
manage.py syncdb creates a django_redirect table in your database. This is a simple lookup table with site_id, old_path and new_path fields.
You can create redirects through either the Django admin interface or the Django database API. For more, see “How to add, change and delete redirects” below.
Once you’ve created redirects, the RedirectFallbackMiddleware does all of the work. Each time any Django application raises a 404 error, this middleware checks the redirects database for the requested URL as a last resort. Specifically, it checks for a redirect with the given old_path with a site ID that corresponds to the SITE_ID setting. (See “Sites” above for more on SITE_ID and the sites framework.) Then, it follows these steps:
The middleware only gets activated for 404s — not for 500s or responses of any other status code.
Note that the order of MIDDLEWARE_CLASSES matters. Generally, you can put RedirectFallbackMiddleware at the end of the list, because it’s a last resort.
If you’ve activated the automatic Django admin interface, you should see a “Redirects” section on the admin index page. Edit redirects as you edit any other object in the system.
Redirects are represented by a standard Django model that lives in django/contrib/redirects/models.py. Hence, you can access redirect objects via the Django database API. For example:
>>> from django.contrib.redirects.models import Redirect >>> from django.contrib.sites.models import Site >>> red = Redirect( ... site=Site.objects.get(id=1), ... old_path='/music/', ... new_path='/sections/arts/music/', ... ) >>> red.save() >>> Redirect.objects.get(old_path='/music/') <Redirect: /music/ ---> /sections/arts/music/>
The django.contrib.csrf package provides easy-to-use protection against Cross-Site Request Forgeries (CSRF).
CSRF, also known as “session riding,” is a Web-site security exploit. It happens when a malicious Web site tricks a user into unknowingly loading a URL from a site at which they’re already authenticated — hence, taking advantage of their authenticated status. This can be a bit tricky to understand at first, so we’ve included two examples here:
Say you’re logged into a webmail account at example.com. Say this webmail site has a “Log out” button that points to the URL example.com/logout — that is, the only action you need to take in order to log out is to visit the page example.com/logout.
A malicious site can coerce you to visit the URL example.com/logout by including that URL as a hidden <iframe> on its own (malicious) page. Thus, if you’re logged into the example.com webmail account and visit the malicious page that has an <iframe> to example.com/logout, the act of visiting the malicious page will log you out from example.com.
Clearly, being logged out of a webmail site against your will is not a terrifying breach of security, but this same type of exploit can happen to any site that “trusts” users — such as bank sites or e-commerce sites.
In previous example, example.com was partially at fault because it allowed a state change (i.e., logging yourself out) to be requested via the HTTP GET method. It’s much better practice to require an HTTP POST for any request that changes state on the server. But even Web sites that require POST for state-changing actions are vulnerable to CSRF.
Say example.com has upgraded its “Log out” functionality so that it’s a <form> button that is requested via POST to the URL example.com/logout. Furthermore, the log-out <form> includes this hidden field:
<input type="hidden" name="confirm" value="true" />
This ensures that a simple POST to the URL example.com/logout won’t perform the logging out; in order for a user to log out, the user must request example.com/logout via POST and send the confirm POST variable with a value of 'true'.
Well, despite the extra security, this arrangement can still be exploited by CSRF; the malicious page just needs to do a little more work. Instead of loading the example.com/logout page in an <iframe>, it can call that URL via POST using JavaScript, passing the confirm=true variable.
How, then, can your site protect itself from this exploit?
The first step is to make sure all GET requests are free of side effects. That way, if a malicious site includes one of your pages as an <iframe>, it won’t have a negative effect.
That leaves POST requests. The second step, then, is to give each POST <form> a hidden field whose value is secret and is generated from the user’s session ID. Then, when processing the form on the server side, check for that secret field and raise an error if it doesn’t validate.
This is exactly what Django’s CSRF prevention layer does.
The django.csrf package contains only one module: middleware.py. This module contains a Django middleware class, CsrfMiddleware, which implements the CSRF protection.
To use it, add 'django.contrib.csrf.middleware.CsrfMiddleware' to the MIDDLEWARE_CLASSES setting in your settings file. This middleware needs to process the response after SessionMiddleware, so CsrfMiddleware must appear before SessionMiddleware in the list. Also, it must process the response before the response gets compressed or otherwise mangled, so CsrfMiddleware must come after GZipMiddleware.
Once you’ve added that to your MIDDLEWARE_CLASSES setting, you’re done. That’s all you need to do.
In case you’re interested, here’s how CsrfMiddleware works. It does these two things:
This ensures that only forms originating from your Web site can be used to POST data back.
This middleware deliberately only targets HTTP POST requests (and the corresponding POST forms). As we explained above, GET requests ought never to have side effects; ensuring this is your own responsibility.
POST requests that are not accompanied by a session cookie are not protected, but they don’t need to be protected, because a malicious Web site could make these kind of requests anyway.
To avoid altering non-textual requests, the middleware checks the response’s Content-Type header before modifying it. Only pages that are served as text/html or application/xml+xhtml are modified.
CsrfMiddleware requires Django’s session framework to work. (See Chapter 12 for more on sessions.) If you’ve using a custom session or authentication framework that manually manages session cookies, this middleware will not help you.
If your app creates HTML pages and forms in some unusual way — e.g., if it sends fragments of HTML in JavaScript document.write statements — you might bypass the filter that adds the hidden field to the form. In this case, the form submission would always fail. (This would happen because the CsrfMiddleware uses a regular expression to add the csrfmiddlewaretoken field to your HTML before the page is sent to the client, and the regular expression sometimes cannot handle wacky HTML.) If you suspect this might be happening, just view source in your Web browser to see whether the csrfmiddlewaretoken was inserted into your <form>.
For more CSRF information and examples, visit http://en.wikipedia.org/wiki/Csrf
This section hasn’t been written yet.
This section hasn’t been written yet.
This section hasn’t been written yet.
This section hasn’t been written yet.
This section hasn’t been written yet.
Comments are closed on this chapter.
We're no longer accepting comments on this version of this chapter.
Many thanks to all those who commented.
About this comment system
This site is using a contextual comment system to help us gather targeted feedback about the book. Instead of commenting on an entire chapter, you can leave comments on any indivdual "block" in the chapter. A "block" with comments looks like this:
A "block" is a paragraph, list item, code sample, or other small chunk of content. It'll get highlighted when you select it:
To post a comment on a block, just click in the gutter next to the bit you want to comment on:
As we edit the book, we'll review everyone's comments and roll them into a future version of the book. We'll mark reviewed comments with a little checkmark:
Please make sure to leave a full name (and not a nickname or screenname) if you'd like your contributions acknowledged in print.
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.extlibrary. Thanks also to Yahoo for YUI itself.