See the guestbook and tutorial examples (~s352978/pub/guestbook.zip and ~s352978/pub/tutorial.zip on dwarf).
Project-App distinction - One project may have several apps. Apps should be written so they may be used in multiple projects. The server is run from the project directory.
Settings: settings.py - All project-specific paths should go here.
URLs-Model-View-Template pattern - Try to keep the URLs separate from the data (the models) separate from what to show based on the data (the views) separate from a description of how to show it (the templates).
Automatically generated administration sites greatly simplify administration.
Key references are The Definitive Guide, Second Edition, Beginning Django E-Commerce, Practical Django Projects, Second Edition, the official Django documentation, and, when necessary, the Python documentation. The texts are available in print and online through the Griffith Library.
Below we describe the concepts introduced in the last lecture in more detail and with more complex examples.
See URL dispatcher in the documentation.
URLconfs (urls.py) map URLs to views, use regular expressions, should be specific to app.
In particular, the pattern (?P<poll_id>\d+) represents a string of one or more digits that may be passed to a view with the keyword parameter poll_id.
To keep apps independent, each app should have its own urls.py module, and the project directory should simply include the app's URLconf. Similarly, the templates for an app myapp should either be in a subdirectory templates of the directory myapp or in a subdirectory templates/myapp of the project directory.
The design of the URL structure for a project is a key design decision by the developer. URLs should be designed to never change - this has been advocated by Tim Berners-Lee since the introduction of the Web.
Use named patterns. This will allow both views and templates to avoid including explicit URL patterns, which allows the developer to change URLs as required without having to change views or templates. Named patterns are used in the function reverse(name) in views and in the tag {% url name %} in templates. The examples show how to do this.
An example URLconf from the tutorial's project directory:
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Enable the user interface
url(r'^polls/', include('polls.urls')),
# Enable the admin interface
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
)
An example URLconf from the tutorial's app directory:
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('polls.views',
url(r'^$', 'index', name='index'),
url(r'^(?P\d+)/$', 'detail', name='detail'),
url(r'^(?P\d+)/results/$', 'results', name='results'),
url(r'^(?P\d+)/vote/$', 'vote', name='vote'),
)
See Writing Models in the documentation.
The design of the models for an app is a key design decision by the developer.
An example model definition file from the tutorial:
from django.db import models
import datetime
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.question
def was_published_today(self):
return self.pub_date.date() == datetime.date.today()
was_published_today.short_description = 'Published today?'
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
votes = models.IntegerField()
def __unicode__(self):
return self.choice
Most models should include a definition of the function get_absolute_url(self). This enables views to redirect to the URL that displays the details of an instance of the model.
The definition of get_absolute_url(self) should not refer explicitly to absolute URLs. Use named patterns to allow this. For example, a definition of model Poll in the tutorial should also include the method definition:
@models.permalink
def get_absolute_url(self):
return ('detail', [str(self.id)])
This method definition allows you to refer to urls in templates using the
{{ poll.get_absolute_url }} construct. Here
'detail' is the name of the URL pattern used to display
details of a single poll.
See the above link to the documentation for more information about models, field types, keyword arguments, foreign keys, many to many relationships, and validity checking.
Recall there is a one-many relationship between polls and choices, and field poll in model Choice is a foreign key reference to model Poll. Then we can refer to the set of choices for a given poll as poll.choice_set.
Suppose there is a many-many relationship between students and courses, and field courses in model Student is a many-to-many reference to model Course. Then we can refer to the set of courses for a given student as student.courses and the set of students in a given course as course.student_set. If the many-many relationship has additional fields, we need to explicitly define a model for the relationship and use a many-to-many field with a through argument, for example:
courses = models.ManyToManyField(Student, through="Enrolment")
Registration of each model in an app with the admin should be in a separate admin.py file in the app.
See Writing Views in the documentation.
Classes: HttpRequest, HttpResponse(template), HttpResponseRedirect(URL).
Methods: render(request, template, context), redirect(URL), get_object_or_404(model, pk=val).
An example view definition file from the tutorial:
from django.shortcuts import render, redirect
from tutorial.polls.models import Poll
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render(request, 'polls/index.html',
{'latest_poll_list': latest_poll_list})
def poll_detail(request, poll_id):
poll = Poll.objects.get(id=poll_id)
return render(request, 'polls/poll_detail.html', {'poll': poll})
To ensure the requested page exists in the last view, it's better to write:
from django.shortcuts import render, redirect, get_object_or_404
def detail(request, poll_id):
poll = get_object_or_404(Poll, pk=poll_id)
return render(request, 'polls/detail.html', {'poll': poll})
This view returns an HTTP 404 response immediately if no poll object with the given id exists.
It's very important that every view that updates the database terminates by redirecting to some (other) URL, to avoid the reload-redo problem:
from django.shortcuts import redirect
def add(request):
"""Add a new item from the posted form data in the request."""
if request.method == "POST"
form = PollForm(request.POST)
if form.is_valid():
# Add a new item from the valid form data
form.save()
# Redirect to the home URL
return redirect('/polls/')
# Handle error conditions
...
Here we assume PollForm is a form class based on class Poll and that /polls/ is the URL of the home page.
However, it is bad practice to use explicit URLs in views and templates. This makes it too difficult to later change the URLs without having to edit many views and templates. So it is better to replace the redirect statement by the following:
return redirect(reverse('index'))
where 'index' is the name of the URL pattern corresponding to
the home page as in the URLconf above.
The convenience shortcut redirect in module django.shortcuts is used here instead of an HttpResponseRedirect constructor.
See The Django template language in the documentation.
Concepts: Comments (single-line comments {# comment #} and multi-line comments {% comment %} ... {% endcomment %}), variables (e.g., {{ poll.id }}), tags (specially conditional and repetitive tags), filters (e.g., {{ author.name|lower }}), inheritance (e.g., {% extends "base.html" %}), file inclusion (e.g., {% include file="header.html" %}).
See my solution to the tutorial problem at ~s352978/pub/tutorial.zipon
dwarf for examples of inheritance. Inheritance is important to avoid
repetition. Good policy is to have three levels of templates: Generic HTML
templates, project-wide templates and page-specific templates, each level
of which extends the previous level. Note that the {% extends
base_template %} tag must be the first tag in each derived
template. Many of my examples use only two levels of inheritance.
Data sanitisation. By default, all variables in templates, especially variables containing user-entered data, are escaped (HTML characters (e.g., <, >, &) are replaced by HTML entities (e.g., <, >, &). This is important to avoid cross-site scripting attacks. If variables are guaranteed safe, use the filter safe, e.g., {{ var|safe }}, to avoid this replacement.
It's important to learn to use the {% url pattern_name pattern_args %} tag and the {{ item.get_absolute_url }} construct to avoid having to write absolute URLs in templates. See examples.
It's important to be consistent with the location of your templates. Either put all templates in a templates subdirectory of the relevant app, or put them in an appropriate templates/app subdirectory of the relevant project. Remember to add to TEMPLATE_DIRS (in the settings file) if necessary.
See Managing static files in the documentation.
See the provided examples.
Note the settings used in settings.py and example templates.
Another approach to managing a collection of static HTML pages
(about.html, policy.html) is to use the
flatpages app in django.contrib. The CMS example from
Chapters 2 and 3 of Practical Django Projects uses this to allow
users to easily create and manage a collection of static HTML pages for a
site. A version of this example is available at ~s352978/pub/cms.zip
on dwarf.
See Working with forms in the documentation.
See the guestbook project (~s352978/pub/guestbook.zip on dwarf).
Data validation. This can be done automatically, specially with forms constructed from models, using model field types. Again, see the guestbook project.
This occurs very frequently in all Web applications. It is a way of displaying a list of objects so that each list item is a link to the details of that object. The general pattern is that a URL of the form /items/ is handled by a view (item_list()) that renders an HTML template of the form:
<ul>
...
<li><a href="/items/n/">Item n</a></li>
...
</ul>
Of course, you would never actually write /items/n/ in a template, you would write {{ item.get_absolute_url }} or {% url item_detail item.id %} or something similar instead. Similarly, Item n would be written {{ item.name }} or similar.
Then, the URL /objects/n/ is handled by a view (object_detail()) that renders an HTML template to display the detail of the object (with id) n.
See my tutorial solution (~s352978/pub/tutorial.zip) for an example.
See the discussion group project (~s352978/pub/groups.zip) for another example, one that uses the list-detail pattern twice.
To be completed...