Massive reorganization of the docs. See the new docs online at http://docs.djangoproject.com/.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8506 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2008-08-23 22:25:40 +00:00
parent b3688e8194
commit 97cb07c3a1
188 changed files with 19913 additions and 17059 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

913
docs/ref/contrib/admin.txt Normal file
View file

@ -0,0 +1,913 @@
.. _ref-contrib-admin:
=====================
The Django admin site
=====================
One of the most powerful parts of Django is the automatic admin interface. It
reads metadata in your model to provide a powerful and production-ready
interface that content producers can immediately use to start adding content to
the site. In this document, we discuss how to activate, use and customize
Django's admin interface.
.. admonition:: Note
The admin site has been refactored significantly since Django 0.96. This
document describes the newest version of the admin site, which allows for
much richer customization. If you follow the development of Django itself,
you may have heard this described as "newforms-admin."
Overview
========
There are five steps in activating the Django admin site:
1. Add ``django.contrib.admin`` to your ``INSTALLED_APPS`` setting.
2. Determine which of your application's models should be editable in the
admin interface.
3. For each of those models, optionally create a ``ModelAdmin`` class that
encapsulates the customized admin functionality and options for that
particular model.
4. Instantiate an ``AdminSite`` and tell it about each of your models and
``ModelAdmin`` classes.
5. Hook the ``AdminSite`` instance into your URLconf.
``ModelAdmin`` objects
======================
The ``ModelAdmin`` class is the representation of a model in the admin
interface. These are stored in a file named ``admin.py`` in your application.
Let's take a look at a very simple example the ``ModelAdmin``::
from django.contrib import admin
from myproject.myapp.models import Author
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
``ModelAdmin`` Options
----------------------
The ``ModelAdmin`` is very flexible. It has several options for dealing with
customizing the interface. All options are defined on the ``ModelAdmin``
subclass::
class AuthorAdmin(admin.ModelAdmin):
date_hierarchy = 'pub_date'
``date_hierarchy``
~~~~~~~~~~~~~~~~~~
Set ``date_hierarchy`` to the name of a ``DateField`` or ``DateTimeField`` in
your model, and the change list page will include a date-based drilldown
navigation by that field.
Example::
date_hierarchy = 'pub_date'
``form``
~~~~~~~~
By default a ``ModelForm`` is dynamically created for your model. It is used
to create the form presented on both the add/change pages. You can easily
provide your own ``ModelForm`` to override any default form behavior on the
add/change pages.
For an example see the section `Adding custom validation to the admin`_.
``fieldsets``
~~~~~~~~~~~~~
Set ``fieldsets`` to control the layout of admin "add" and "change" pages.
``fieldsets`` is a list of two-tuples, in which each two-tuple represents a
``<fieldset>`` on the admin form page. (A ``<fieldset>`` is a "section" of the
form.)
The two-tuples are in the format ``(name, field_options)``, where ``name`` is a
string representing the title of the fieldset and ``field_options`` is a
dictionary of information about the fieldset, including a list of fields to be
displayed in it.
A full example, taken from the ``django.contrib.flatpages.FlatPage`` model::
class FlatPageAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('url', 'title', 'content', 'sites')
}),
('Advanced options', {
'classes': ('collapse',),
'fields': ('enable_comments', 'registration_required', 'template_name')
}),
)
This results in an admin page that looks like:
.. image:: _images/flatfiles_admin.png
If ``fieldsets`` isn't given, Django will default to displaying each field
that isn't an ``AutoField`` and has ``editable=True``, in a single fieldset,
in the same order as the fields are defined in the model.
The ``field_options`` dictionary can have the following keys:
``fields``
A tuple of field names to display in this fieldset. This key is required.
Example::
{
'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
}
To display multiple fields on the same line, wrap those fields in their own
tuple. In this example, the ``first_name`` and ``last_name`` fields will
display on the same line::
{
'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
}
``classes``
A list containing extra CSS classes to apply to the fieldset.
Example::
{
'classes': ['wide', 'extrapretty'],
}
Two useful classes defined by the default admin-site stylesheet are
``collapse`` and ``wide``. Fieldsets with the ``collapse`` style will be
initially collapsed in the admin and replaced with a small "click to expand"
link. Fieldsets with the ``wide`` style will be given extra horizontal space.
``description``
A string of optional extra text to be displayed at the top of each fieldset,
under the heading of the fieldset.
Note that this value is *not* HTML-escaped when it's displayed in
the admin interface. This lets you include HTML if you so desire.
Alternatively you can use plain text and
``django.utils.html.escape()`` to escape any HTML special
characters.
``fields``
~~~~~~~~~~
Use this option as an alternative to ``fieldsets`` if the layout does not
matter and if you want to only show a subset of the available fields in the
form. For example, you could define a simpler version of the admin form for
the ``django.contrib.flatpages.FlatPage`` model as follows::
class FlatPageAdmin(admin.ModelAdmin):
fields = ('url', 'title', 'content')
In the above example, only the fields 'url', 'title' and 'content' will be
displayed, sequentially, in the form.
.. admonition:: Note
This ``fields`` option should not be confused with the ``fields``
dictionary key that is within the ``fieldsets`` option, as described in
the previous section.
``filter_horizontal``
~~~~~~~~~~~~~~~~~~~~~
Use a nifty unobtrusive JavaScript "filter" interface instead of the
usability-challenged ``<select multiple>`` in the admin form. The value is a
list of fields that should be displayed as a horizontal filter interface. See
``filter_vertical`` to use a vertical interface.
``filter_vertical``
~~~~~~~~~~~~~~~~~~~
Same as ``filter_horizontal``, but is a vertical display of the filter
interface.
``list_display``
~~~~~~~~~~~~~~~~
Set ``list_display`` to control which fields are displayed on the change list
page of the admin.
Example::
list_display = ('first_name', 'last_name')
If you don't set ``list_display``, the admin site will display a single column
that displays the ``__unicode__()`` representation of each object.
You have four possible values that can be used in ``list_display``:
* A field of the model. For example::
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name')
* A callable that accepts one parameter for the model instance. For
example::
def upper_case_name(obj):
return "%s %s" % (obj.first_name, obj.last_name).upper()
upper_case_name.short_description = 'Name'
class PersonAdmin(admin.ModelAdmin):
list_display = (upper_case_name,)
* A string representing an attribute on the ``ModelAdmin``. This behaves
the same as the callable. For example::
class PersonAdmin(admin.ModelAdmin):
list_display = ('upper_case_name',)
def upper_case_name(self, obj):
return "%s %s" % (obj.first_name, obj.last_name).upper()
upper_case_name.short_description = 'Name'
* A string representing an attribute on the model. This behaves almost
the same as the callable, but ``self`` in this context is the model
instance. Here's a full model example::
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'decade_born_in')
A few special cases to note about ``list_display``:
* If the field is a ``ForeignKey``, Django will display the
``__unicode__()`` of the related object.
* ``ManyToManyField`` fields aren't supported, because that would entail
executing a separate SQL statement for each row in the table. If you
want to do this nonetheless, give your model a custom method, and add
that method's name to ``list_display``. (See below for more on custom
methods in ``list_display``.)
* If the field is a ``BooleanField`` or ``NullBooleanField``, Django will
display a pretty "on" or "off" icon instead of ``True`` or ``False``.
* If the string given is a method of the model, ``ModelAdmin`` or a
callable, Django will HTML-escape the output by default. If you'd rather
not escape the output of the method, give the method an ``allow_tags``
attribute whose value is ``True``.
Here's a full example model::
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def colored_name(self):
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
colored_name.allow_tags = True
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'colored_name')
* If the string given is a method of the model, ``ModelAdmin`` or a
callable that returns True or False Django will display a pretty "on" or
"off" icon if you give the method a ``boolean`` attribute whose value is
``True``.
Here's a full example model::
class Person(models.Model):
first_name = models.CharField(max_length=50)
birthday = models.DateField()
def born_in_fifties(self):
return self.birthday.strftime('%Y')[:3] == 5
born_in_fifties.boolean = True
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'born_in_fifties')
* The ``__str__()`` and ``__unicode__()`` methods are just as valid in
``list_display`` as any other model method, so it's perfectly OK to do
this::
list_display = ('__unicode__', 'some_other_field')
* Usually, elements of ``list_display`` that aren't actual database fields
can't be used in sorting (because Django does all the sorting at the
database level).
However, if an element of ``list_display`` represents a certain database
field, you can indicate this fact by setting the ``admin_order_field``
attribute of the item.
For example::
class Person(models.Model):
first_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def colored_first_name(self):
return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
colored_first_name.allow_tags = True
colored_first_name.admin_order_field = 'first_name'
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'colored_first_name')
The above will tell Django to order by the ``first_name`` field when
trying to sort by ``colored_first_name`` in the admin.
``list_display_links``
~~~~~~~~~~~~~~~~~~~~~~
Set ``list_display_links`` to control which fields in ``list_display`` should
be linked to the "change" page for an object.
By default, the change list page will link the first column -- the first field
specified in ``list_display`` -- to the change page for each item. But
``list_display_links`` lets you change which columns are linked. Set
``list_display_links`` to a list or tuple of field names (in the same format as
``list_display``) to link.
``list_display_links`` can specify one or many field names. As long as the
field names appear in ``list_display``, Django doesn't care how many (or how
few) fields are linked. The only requirement is: If you want to use
``list_display_links``, you must define ``list_display``.
In this example, the ``first_name`` and ``last_name`` fields will be linked on
the change list page::
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'birthday')
list_display_links = ('first_name', 'last_name')
Finally, note that in order to use ``list_display_links``, you must define
``list_display``, too.
``list_filter``
~~~~~~~~~~~~~~~
Set ``list_filter`` to activate filters in the right sidebar of the change list
page of the admin. This should be a list of field names, and each specified
field should be either a ``BooleanField``, ``CharField``, ``DateField``,
``DateTimeField``, ``IntegerField`` or ``ForeignKey``.
This example, taken from the ``django.contrib.auth.models.User`` model, shows
how both ``list_display`` and ``list_filter`` work::
class UserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser')
The above code results in an admin change list page that looks like this:
.. image:: _images/users_changelist.png
(This example also has ``search_fields`` defined. See below.)
``list_per_page``
~~~~~~~~~~~~~~~~~
Set ``list_per_page`` to control how many items appear on each paginated admin
change list page. By default, this is set to ``100``.
``list_select_related``
~~~~~~~~~~~~~~~~~~~~~~~
Set ``list_select_related`` to tell Django to use ``select_related()`` in
retrieving the list of objects on the admin change list page. This can save you
a bunch of database queries.
The value should be either ``True`` or ``False``. Default is ``False``.
Note that Django will use ``select_related()``, regardless of this setting,
if one of the ``list_display`` fields is a ``ForeignKey``.
For more on ``select_related()``, see
:ref:`the select_related() docs <select-related>`.
``inlines``
~~~~~~~~~~~
See ``InlineModelAdmin`` objects below.
``ordering``
~~~~~~~~~~~~
Set ``ordering`` to specify how objects on the admin change list page should be
ordered. This should be a list or tuple in the same format as a model's
``ordering`` parameter.
If this isn't provided, the Django admin will use the model's default ordering.
.. admonition:: Note
Django will only honor the first element in the list/tuple; any others
will be ignored.
``prepopulated_fields``
~~~~~~~~~~~~~~~~~~~~~~~
Set ``prepopulated_fields`` to a dictionary mapping field names to the fields
it should prepopulate from::
class ArticleAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
When set, the given fields will use a bit of JavaScript to populate from the
fields assigned. The main use for this functionality is to automatically
generate the value for ``SlugField`` fields from one or more other fields. The
generated value is produced by concatenating the values of the source fields,
and then by transforming that result into a valid slug (e.g. substituting
dashes for spaces).
``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``, nor
``ManyToManyField`` fields.
``radio_fields``
~~~~~~~~~~~~~~~~
By default, Django's admin uses a select-box interface (<select>) for
fields that are ``ForeignKey`` or have ``choices`` set. If a field is present
in ``radio_fields``, Django will use a radio-button interface instead.
Assuming ``group`` is a ``ForeignKey`` on the ``Person`` model::
class PersonAdmin(admin.ModelAdmin):
radio_fields = {"group": admin.VERTICAL}
You have the choice of using ``HORIZONTAL`` or ``VERTICAL`` from the
``django.contrib.admin`` module.
Don't include a field in ``radio_fields`` unless it's a ``ForeignKey`` or has
``choices`` set.
``raw_id_fields``
~~~~~~~~~~~~~~~~~
By default, Django's admin uses a select-box interface (<select>) for
fields that are ``ForeignKey``. Sometimes you don't want to incur the
overhead of having to select all the related instances to display in the
drop-down.
``raw_id_fields`` is a list of fields you would like to change
into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
class ArticleAdmin(admin.ModelAdmin):
raw_id_fields = ("newspaper",)
``save_as``
~~~~~~~~~~~
Set ``save_as`` to enable a "save as" feature on admin change forms.
Normally, objects have three save options: "Save", "Save and continue editing"
and "Save and add another". If ``save_as`` is ``True``, "Save and add another"
will be replaced by a "Save as" button.
"Save as" means the object will be saved as a new object (with a new ID),
rather than the old object.
By default, ``save_as`` is set to ``False``.
``save_on_top``
~~~~~~~~~~~~~~~
Set ``save_on_top`` to add save buttons across the top of your admin change
forms.
Normally, the save buttons appear only at the bottom of the forms. If you set
``save_on_top``, the buttons will appear both on the top and the bottom.
By default, ``save_on_top`` is set to ``False``.
``search_fields``
~~~~~~~~~~~~~~~~~
Set ``search_fields`` to enable a search box on the admin change list page.
This should be set to a list of field names that will be searched whenever
somebody submits a search query in that text box.
These fields should be some kind of text field, such as ``CharField`` or
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
the lookup API "follow" notation::
search_fields = ['foreign_key__related_fieldname']
When somebody does a search in the admin search box, Django splits the search
query into words and returns all objects that contain each of the words, case
insensitive, where each word must be in at least one of ``search_fields``. For
example, if ``search_fields`` is set to ``['first_name', 'last_name']`` and a
user searches for ``john lennon``, Django will do the equivalent of this SQL
``WHERE`` clause::
WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')
For faster and/or more restrictive searches, prefix the field name
with an operator:
``^``
Matches the beginning of the field. For example, if ``search_fields`` is
set to ``['^first_name', '^last_name']`` and a user searches for
``john lennon``, Django will do the equivalent of this SQL ``WHERE``
clause::
WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%')
AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')
This query is more efficient than the normal ``'%john%'`` query, because
the database only needs to check the beginning of a column's data, rather
than seeking through the entire column's data. Plus, if the column has an
index on it, some databases may be able to use the index for this query,
even though it's a ``LIKE`` query.
``=``
Matches exactly, case-insensitive. For example, if
``search_fields`` is set to ``['=first_name', '=last_name']`` and
a user searches for ``john lennon``, Django will do the equivalent
of this SQL ``WHERE`` clause::
WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john')
AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')
Note that the query input is split by spaces, so, following this example,
it's currently not possible to search for all records in which
``first_name`` is exactly ``'john winston'`` (containing a space).
``@``
Performs a full-text match. This is like the default search method but uses
an index. Currently this is only available for MySQL.
``ModelAdmin`` methods
----------------------
``save_model(self, request, obj, form, change)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``save_model`` method is given the ``HttpRequest``, a model instance,
a ``ModelForm`` instance and a boolean value based on whether it is adding or
changing the object. Here you can do any pre- or post-save operations.
For example to attach ``request.user`` to the object prior to saving::
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
``save_formset(self, request, form, formset, change)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``save_formset`` method is given the ``HttpRequest``, the parent
``ModelForm`` instance and a boolean value based on whether it is adding or
changing the parent object.
For example to attach ``request.user`` to each changed formset
model instance::
class ArticleAdmin(admin.ModelAdmin):
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
formset.save_m2m()
``ModelAdmin`` media definitions
--------------------------------
There are times where you would like add a bit of CSS and/or JavaScript to
the add/change views. This can be accomplished by using a Media inner class
on your ``ModelAdmin``::
class ArticleAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ("my_styles.css",)
}
js = ("my_code.js",)
Keep in mind that this will be prepended with ``MEDIA_URL``. The same rules
apply as :ref:`regular media definitions on forms <topics-forms-media>`.
Adding custom validation to the admin
-------------------------------------
Adding custom validation of data in the admin is quite easy. The automatic admin
interfaces reuses :mod:`django.forms`, and the ``ModelAdmin`` class gives you
the ability define your own form::
class ArticleAdmin(admin.ModelAdmin):
form = MyArticleAdminForm
``MyArticleAdminForm`` can be defined anywhere as long as you import where
needed. Now within your form you can add your own custom validation for
any field::
class MyArticleAdminForm(forms.ModelForm):
class Meta:
model = Article
def clean_name(self):
# do something that validates your data
return self.cleaned_data["name"]
It is important you use a ``ModelForm`` here otherwise things can break. See the
:ref:`forms <ref-forms-index>` documentation on :ref:`custom validation
<ref-forms-validation>` for more information.
``InlineModelAdmin`` objects
============================
The admin interface has the ability to edit models on the same page as a
parent model. These are called inlines. You can add them to a model by
specifying them in a ``ModelAdmin.inlines`` attribute::
class BookInline(admin.TabularInline):
model = Book
class AuthorAdmin(admin.ModelAdmin):
inlines = [
BookInline,
]
Django provides two subclasses of ``InlineModelAdmin`` and they are:
* ``TabularInline``
* ``StackedInline``
The difference between these two is merely the template used to render them.
``InlineModelAdmin`` options
-----------------------------
The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits
all the same functionality as well as some of its own:
``model``
~~~~~~~~~
The model in which the inline is using. This is required.
``fk_name``
~~~~~~~~~~~
The name of the foreign key on the model. In most cases this will be dealt
with automatically, but ``fk_name`` must be specified explicitly if there are
more than one foreign key to the same parent model.
``formset``
~~~~~~~~~~~
This defaults to ``BaseInlineFormSet``. Using your own formset can give you
many possibilities of customization. Inlines are built around
:ref:`model formsets <model-formsets>`.
``form``
~~~~~~~~
The value for ``form`` is inherited from ``ModelAdmin``. This is what is
passed through to ``formset_factory`` when creating the formset for this
inline.
``extra``
~~~~~~~~~
This controls the number of extra forms the formset will display in addition
to the initial forms. See the
:ref:`formsets documentation <topics-forms-formsets>` for more information.
``max_num``
~~~~~~~~~~~
This controls the maximum number of forms to show in the inline. This doesn't
directly correlate to the number of objects, but can if the value is small
enough. See :ref:`model-formsets-max-num` for more information.
``raw_id_fields``
~~~~~~~~~~~~~~~~~
By default, Django's admin uses a select-box interface (<select>) for
fields that are ``ForeignKey``. Sometimes you don't want to incur the
overhead of having to select all the related instances to display in the
drop-down.
``raw_id_fields`` is a list of fields you would like to change
into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
class BookInline(admin.TabularInline):
model = Book
raw_id_fields = ("pages",)
``template``
~~~~~~~~~~~~
The template used to render the inline on the page.
``verbose_name``
~~~~~~~~~~~~~~~~
An override to the ``verbose_name`` found in the model's inner ``Meta`` class.
``verbose_name_plural``
~~~~~~~~~~~~~~~~~~~~~~~
An override to the ``verbose_name_plural`` found in the model's inner ``Meta``
class.
Working with a model with two or more foreign keys to the same parent model
---------------------------------------------------------------------------
It is sometimes possible to have more than one foreign key to the same model.
Take this model for instance::
class Friendship(models.Model):
to_person = models.ForeignKey(Person, related_name="friends")
from_person = models.ForeignKey(Person, related_name="from_friends")
If you wanted to display an inline on the ``Person`` admin add/change pages
you need to explicitly define the foreign key since it is unable to do so
automatically::
class FriendshipInline(admin.TabularInline):
model = Friendship
fk_name = "to_person"
class PersonAdmin(admin.ModelAdmin):
inlines = [
FriendshipInline,
]
Working with Many-to-Many Intermediary Models
----------------------------------------------
By default, admin widgets for many-to-many relations will be displayed inline
on whichever model contains the actual reference to the ``ManyToManyField``.
However, when you specify an intermediary model using the ``through``
argument to a ``ManyToManyField``, the admin will not display a widget by
default. This is because each instance of that intermediary model requires
more information than could be displayed in a single widget, and the layout
required for multiple widgets will vary depending on the intermediate model.
However, we still want to be able to edit that information inline. Fortunately,
this is easy to do with inline admin models. Suppose we have the following
models::
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
The first step in displaying this intermediate model in the admin is to
define an inline class for the ``Membership`` model::
class MembershipInline(admin.TabularInline):
model = Membership
extra = 1
This simple example uses the default ``InlineModelAdmin`` values for the
``Membership`` model, and limits the extra add forms to one. This could be
customized using any of the options available to ``InlineModelAdmin`` classes.
Now create admin views for the ``Person`` and ``Group`` models::
class PersonAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
Finally, register your ``Person`` and ``Group`` models with the admin site::
admin.site.register(Person, PersonAdmin)
admin.site.register(Group, GroupAdmin)
Now your admin site is set up to edit ``Membership`` objects inline from
either the ``Person`` or the ``Group`` detail pages.
Using generic relations as an inline
------------------------------------
It is possible to use an inline with generically related objects. Let's say
you have the following models::
class Image(models.Model):
image = models.ImageField(upload_to="images")
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
class Product(models.Model):
name = models.CharField(max_length=100)
If you want to allow editing and creating ``Image`` instance on the ``Product``
add/change views you can simply use ``GenericInlineModelAdmin`` provided by
``django.contrib.contenttypes.generic``. In your ``admin.py`` for this
example app::
from django.contrib import admin
from django.contrib.contenttypes import generic
from myproject.myapp.models import Image, Product
class ImageInline(generic.GenericTabularInline):
model = Image
class ProductAdmin(admin.ModelAdmin):
inlines = [
ImageInline,
]
admin.site.register(Product, ProductAdmin)
``django.contrib.contenttypes.generic`` provides both a ``GenericTabularInline``
and ``GenericStackedInline`` and behave just like any other inline. See the
:ref:`contenttypes documentation <ref-contrib-contenttypes>` for more specific
information.
``AdminSite`` objects
=====================
Hooking ``AdminSite`` instances into your URLconf
-------------------------------------------------
The last step in setting up the Django admin is to hook your ``AdminSite``
instance into your URLconf. Do this by pointing a given URL at the
``AdminSite.root`` method.
In this example, we register the default ``AdminSite`` instance
``django.contrib.admin.site`` at the URL ``/admin/`` ::
# urls.py
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
('^admin/(.*)', admin.site.root),
)
Above we used ``admin.autodiscover()`` to automatically load the
``INSTALLED_APPS`` admin.py modules.
In this example, we register the ``AdminSite`` instance
``myproject.admin.admin_site`` at the URL ``/myadmin/`` ::
# urls.py
from django.conf.urls.defaults import *
from myproject.admin import admin_site
urlpatterns = patterns('',
('^myadmin/(.*)', admin_site.root),
)
There is really no need to use autodiscover when using your own ``AdminSite``
instance since you will likely be importing all the per-app admin.py modules
in your ``myproject.admin`` module.
Note that the regular expression in the URLpattern *must* group everything in
the URL that comes after the URL root -- hence the ``(.*)`` in these examples.
Multiple admin sites in the same URLconf
----------------------------------------
It's easy to create multiple instances of the admin site on the same
Django-powered Web site. Just create multiple instances of ``AdminSite`` and
root each one at a different URL.
In this example, the URLs ``/basic-admin/`` and ``/advanced-admin/`` feature
separate versions of the admin site -- using the ``AdminSite`` instances
``myproject.admin.basic_site`` and ``myproject.admin.advanced_site``,
respectively::
# urls.py
from django.conf.urls.defaults import *
from myproject.admin import basic_site, advanced_site
urlpatterns = patterns('',
('^basic-admin/(.*)', basic_site.root),
('^advanced-admin/(.*)', advanced_site.root),
)

View file

@ -0,0 +1,6 @@
.. _ref-contrib-auth:
``django.contrib.auth``
=======================
See :ref:`topics-auth`.

View file

@ -0,0 +1,347 @@
.. _ref-contrib-contenttypes:
==========================
The contenttypes framework
==========================
.. module:: django.contrib.contenttypes
:synopsis: Provides generic interface to installed models.
Django includes a :mod:`contenttypes` application that can track all of
the models installed in your Django-powered project, providing a
high-level, generic interface for working with your models.
Overview
========
At the heart of the contenttypes application is the
:class:`~django.contrib.contenttypes.models.ContentType` model, which lives at
``django.contrib.contenttypes.models.ContentType``. Instances of
:class:`~django.contrib.contenttypes.models.ContentType` represent and store
information about the models installed in your project, and new instances of
:class:`~django.contrib.contenttypes.models.ContentType` are automatically
created whenever new models are installed.
Instances of :class:`~django.contrib.contenttypes.models.ContentType` have
methods for returning the model classes they represent and for querying objects
from those models. :class:`~django.contrib.contenttypes.models.ContentType`
also has a :ref:`custom manager <custom-managers>` that adds methods for
working with :class:`~django.contrib.contenttypes.models.ContentType` and for
obtaining instances of :class:`~django.contrib.contenttypes.models.ContentType`
for a particular model.
Relations between your models and
:class:`~django.contrib.contenttypes.models.ContentType` can also be used to
enable "generic" relationships between an instance of one of your
models and instances of any model you have installed.
Installing the contenttypes framework
=====================================
The contenttypes framework is included in the default
:setting:`INSTALLED_APPS` list created by ``django-admin.py startproject``,
but if you've removed it or if you manually set up your
:setting:`INSTALLED_APPS` list, you can enable it by adding
``'django.contrib.contenttypes'`` to your :setting:`INSTALLED_APPS` setting.
It's generally a good idea to have the contenttypes framework
installed; several of Django's other bundled applications require it:
* The admin application uses it to log the history of each object
added or changed through the admin interface.
* Django's :mod:`authentication framework <django.contrib.auth>` uses it
to tie user permissions to specific models.
* Django's comments system (:mod:`django.contrib.comments`) uses it to
"attach" comments to any installed model.
The ``ContentType`` model
=========================
.. class:: models.ContentType
Each instance of :class:`~django.contrib.contenttypes.models.ContentType`
has three fields which, taken together, uniquely describe an installed model:
.. attribute:: models.ContentType.app_label
The name of the application the model is part of. This is taken from
the :attr:`app_label` attribute of the model, and includes only the *last*
part of the application's Python import path;
"django.contrib.contenttypes", for example, becomes an :attr:`app_label`
of "contenttypes".
.. attribute:: models.ContentType.model
The name of the model class.
.. attribute:: models.ContentType.name
The human-readable name of the model. This is taken from the
:attr:`verbose_name <django.db.models.fields.Field.verbose_name>`
attribute of the model.
Let's look at an example to see how this works. If you already have
the contenttypes application installed, and then add
:mod:`the sites application <django.contrib.sites>` to your
:setting:`INSTALLED_APPS` setting and run ``manage.py syncdb`` to install it,
the model :class:`django.contrib.sites.models.Site` will be installed into
your database. Along with it a new instance of
:class:`~django.contrib.contenttypes.models.ContentType` will be
created with the following values:
* :attr:`app_label` will be set to ``'sites'`` (the last part of the Python
path "django.contrib.sites").
* :attr:`model` will be set to ``'site'``.
* :attr:`name` will be set to ``'site'``.
.. _the verbose_name attribute: ../model-api/#verbose_name
Methods on ``ContentType`` instances
====================================
.. class:: models.ContentType
Each :class:`~django.contrib.contenttypes.models.ContentType` instance has
methods that allow you to get from a
:class:`~django.contrib.contenttypes.models.ContentType` instance to the model
it represents, or to retrieve objects from that model:
.. method:: models.ContentType.get_object_for_this_type(**kwargs)
Takes a set of valid :ref:`lookup arguments <field-lookups-intro>` for the
model the :class:`~django.contrib.contenttypes.models.ContentType`
represents, and does :ref:`a get() lookup <get-kwargs>` on that model,
returning the corresponding object.
.. method:: models.ContentType.model_class()
Returns the model class represented by this
:class:`~django.contrib.contenttypes.models.ContentType` instance.
For example, we could look up the
:class:`~django.contrib.contenttypes.models.ContentType` for the
:class:`~django.contrib.auth.models.User` model::
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
>>> user_type
<ContentType: user>
And then use it to query for a particular ``User``, or to get access
to the ``User`` model class::
>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
Together,
:meth:`~django.contrib.contenttypes.models.ContentType.get_object_for_this_type`
and :meth:`~django.contrib.contenttypes.models.ContentType.model_class`
enable two extremely important use cases:
1. Using these methods, you can write high-level generic code that
performs queries on any installed model -- instead of importing and using
a single specific model class, you can pass an ``app_label`` and
``model`` into a :class:`~django.contrib.contenttypes.models.ContentType`
lookup at runtime, and then work with the model class or retrieve objects
from it.
2. You can relate another model to
:class:`~django.contrib.contenttypes.models.ContentType` as a way of
tying instances of it to particular model classes, and use these methods
to get access to those model classes.
Several of Django's bundled applications make use of the latter technique.
For example,
:class:`the permissions system <django.contrib.auth.models.Permission` in
Django's authentication framework uses a
:class:`~django.contrib.auth.models.Permission` model with a foreign
key to :class:`~django.contrib.contenttypes.models.ContentType`; this lets
:class:`~django.contrib.auth.models.Permission` represent concepts like
"can add blog entry" or "can delete news story".
The ``ContentTypeManager``
--------------------------
.. class:: models.ContentTypeManager
:class:`~django.contrib.contenttypes.models.ContentType` also has a custom
manager, :class:`~django.contrib.contenttypes.models.ContentTypeManager`,
which adds the following methods:
.. method:: models.ContentTypeManager.clear_cache()
Clears an internal cache used by
:class:`~django.contrib.contenttypes.models.ContentType>` to keep track
of which models for which it has created
:class:`django.contrib.contenttypes.models.ContentType>` instances. You
probably won't ever need to call this method yourself; Django will call
it automatically when it's needed.
.. method:: models.ContentTypeManager.get_for_model(model)
Takes either a model class or an instance of a model, and returns the
:class:`~django.contrib.contenttypes.models.ContentType` instance
representing that model.
The :meth:`~models.ContentTypeManager.get_for_model()` method is especially useful when you know you
need to work with a :class:`ContentType <django.contrib.contenttypes.models.ContentType>` but don't want to go to the
trouble of obtaining the model's metadata to perform a manual lookup::
>>> from django.contrib.auth.models import User
>>> user_type = ContentType.objects.get_for_model(User)
>>> user_type
<ContentType: user>
.. _generic-relations:
Generic relations
=================
Adding a foreign key from one of your own models to
:class:`~django.contrib.contenttypes.models.ContentType` allows your model to
effectively tie itself to another model class, as in the example of the
:class:`~django.contrib.auth.models.Permission` model above. But it's possible
to go one step further and use
:class:`~django.contrib.contenttypes.models.ContentType` to enable truly
generic (sometimes called "polymorphic") relationships between models.
A simple example is a tagging system, which might look like this::
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag
A normal :class:`~django.db.models.fields.related.ForeignKey` can only "point
to" one other model, which means that if the ``TaggedItem`` model used a
:class:`~django.db.models.fields.related.ForeignKey` it would have to
choose one and only one model to store tags for. The contenttypes
application provides a special field type --
:class:`django.contrib.contenttypes.generic.GenericForeignKey` -- which
works around this and allows the relationship to be with any
model. There are three parts to setting up a
:class:`~django.contrib.contenttypes.generic.GenericForeignKey`:
1. Give your model a :class:`~django.db.models.fields.related.ForeignKey`
to :class:`~django.contrib.contenttypes.models.ContentType`.
2. Give your model a field that can store a primary-key value from the
models you'll be relating to. (For most models, this means an
:class:`~django.db.models.fields.IntegerField` or
:class:`~django.db.models.fields.PositiveIntegerField`.)
This field must be of the same type as the primary key of the models
that will be involved in the generic relation. For example, if you use
:class:`~django.db.models.fields.IntegerField`, you won't be able to
form a generic relation with a model that uses a
:class:`~django.db.models.fields.CharField` as a primary key.
3. Give your model a
:class:`~django.contrib.contenttypes.generic.GenericForeignKey`, and
pass it the names of the two fields described above. If these fields
are named "content_type" and "object_id", you can omit this -- those
are the default field names
:class:`~django.contrib.contenttypes.generic.GenericForeignKey` will
look for.
This will enable an API similar to the one used for a normal
:class:`~django.db.models.fields.related.ForeignKey`;
each ``TaggedItem`` will have a ``content_object`` field that returns the
object it's related to, and you can also assign to that field or use it when
creating a ``TaggedItem``::
>>> from django.contrib.auth.models import User
>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()
>>> t.content_object
<User: Guido>
Due to the way :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
is implemented, you cannot use such fields directly with filters (``filter()``
and ``exclude()``, for example) via the database API. They aren't normal field
objects. These examples will *not* work::
# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
Reverse generic relations
-------------------------
If you know which models you'll be using most often, you can also add
a "reverse" generic relationship to enable an additional API. For example::
class Bookmark(models.Model):
url = models.URLField()
tags = generic.GenericRelation(TaggedItem)
``Bookmark`` instances will each have a ``tags`` attribute, which can
be used to retrieve their associated ``TaggedItems``::
>>> b = Bookmark(url='http://www.djangoproject.com/')
>>> b.save()
>>> t1 = TaggedItem(content_object=b, tag='django')
>>> t1.save()
>>> t2 = TaggedItem(content_object=b, tag='python')
>>> t2.save()
>>> b.tags.all()
[<TaggedItem: django>, <TaggedItem: python>]
If you don't add the reverse relationship, you can do the lookup manually::
>>> b = Bookmark.objects.get(url='http://www.djangoproject.com/)
>>> bookmark_type = ContentType.objects.get_for_model(b)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id,
... object_id=b.id)
[<TaggedItem: django>, <TaggedItem: python>]
Note that if you delete an object that has a
:class:`~django.contrib.contenttypes.generic.GenericRelation`, any objects
which have a :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
pointing at it will be deleted as well. In the example above, this means that
if a ``Bookmark`` object were deleted, any ``TaggedItem`` objects pointing at
it would be deleted at the same time.
Generic relations in forms and admin
------------------------------------
:mod:`django.contrib.contenttypes.generic` provides both a
:class:`~django.contrib.contenttypes.generic.GenericInlineFormSet`
and :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`.
This enables the use of generic relations in forms and the admin. See the
:ref:`model formset <topics-forms-modelforms>` and
:ref:`admin <ref-contrib-admin>` documentation for more information.
.. class:: generic.GenericInlineModelAdmin
The :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`
class inherits all properties from an
:class:`~django.contrib.admin.options.InlineModelAdmin` class. However,
it adds a couple of its own for working with the generic relation:
.. attribute:: generic.GenericInlineModelAdmin.ct_field
The name of the
:class:`~django.contrib.contenttypes.models.ContentType` foreign key
field on the model. Defaults to ``content_type``.
.. attribute:: generic.GenericInlineModelAdmin.ct_fk_field
The name of the integer field that represents the ID of the related
object. Defaults to ``object_id``.

76
docs/ref/contrib/csrf.txt Normal file
View file

@ -0,0 +1,76 @@
.. _ref-contrib-csrf:
=====================================
Cross Site Request Forgery protection
=====================================
.. module:: django.contrib.csrf
:synopsis: Protects against Cross Site Request Forgeries
The CsrfMiddleware class provides easy-to-use protection against
`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
Web site creates a link or form button that is intended to perform some action
on your Web site, using the credentials of a logged-in user who is tricked
into clicking on the link in their browser.
The first defense against CSRF attacks is to ensure that GET requests
are side-effect free. POST requests can then be protected by adding this
middleware into your list of installed middleware.
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
How to use it
=============
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
your list of middleware classes, :setting:`MIDDLEWARE_CLASSES`. It needs to process
the response after the SessionMiddleware, so must come before it in the
list. It also must process the response before things like compression
happen to the response, so it must come after GZipMiddleware in the list.
How it works
============
CsrfMiddleware does two things:
1. It modifies outgoing requests by adding a hidden form field to all
'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
a hash of the session ID plus a secret. If there is no session ID set,
this modification of the response isn't done, so there is very little
performance penalty for those requests that don't have a session.
2. On all incoming POST requests that have the session cookie set, it
checks that the 'csrfmiddlewaretoken' is present and correct. If it
isn't, the user will get a 403 error.
This ensures that only forms that have originated from your Web site
can be used to POST data back.
It deliberately only targets HTTP POST requests (and the corresponding POST
forms). GET requests ought never to have any potentially dangerous side
effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a
CSRF attack with a GET request ought to be harmless.
POST requests that are not accompanied by a session cookie are not protected,
but they do not need to be protected, since the 'attacking' Web site
could make these kind of requests anyway.
The Content-Type is checked before modifying the response, and only
pages that are served as 'text/html' or 'application/xml+xhtml'
are modified.
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Limitations
===========
CsrfMiddleware requires Django's session framework to work. If you have
a custom authentication system that manually sets cookies and the like,
it won't help you.
If your app creates HTML pages and forms in some unusual way, (e.g.
it sends fragments of HTML in JavaScript document.write statements)
you might bypass the filter that adds the hidden field to the form,
in which case form submission will always fail. It may still be possible
to use the middleware, provided you can find some way to get the
CSRF token and ensure that is included when your form is submitted.

View file

@ -0,0 +1,92 @@
.. _ref-contrib-databrowse:
==========
Databrowse
==========
.. module:: django.contrib.databrowse
:synopsis: Databrowse is a Django application that lets you browse your data.
Databrowse is a Django application that lets you browse your data.
As the Django admin dynamically creates an admin interface by introspecting
your models, Databrowse dynamically creates a rich, browsable Web site by
introspecting your models.
.. admonition:: Note
Databrowse is **very** new and is currently under active development. It
may change substantially before the next Django release.
With that said, it's easy to use, and it doesn't require writing any
code. So you can play around with it today, with very little investment in
time or coding.
How to use Databrowse
=====================
1. Point Django at the default Databrowse templates. There are two ways to
do this:
* Add ``'django.contrib.databrowse'`` to your :setting:`INSTALLED_APPS`
setting. This will work if your :setting:`TEMPLATE_LOADERS` setting
includes the ``app_directories`` template loader (which is the case by
default). See the :ref:`template loader docs <template-loaders>` for
more.
* Otherwise, determine the full filesystem path to the
`:file:`django/contrib/databrowse/templates` directory, and add that
directory to your :setting:`TEMPLATE_DIRS` setting.
2. Register a number of models with the Databrowse site::
from django.contrib import databrowse
from myapp.models import SomeModel, SomeOtherModel
databrowse.site.register(SomeModel)
databrowse.site.register(SomeOtherModel)
Note that you should register the model *classes*, not instances.
It doesn't matter where you put this, as long as it gets executed at some
point. A good place for it is in your :ref:`URLconf file
<topics-http-urls>` (``urls.py``).
3. Change your URLconf to import the :mod:`~django.contrib.databrowse` module::
from django.contrib import databrowse
...and add the following line to your URLconf::
(r'^databrowse/(.*)', databrowse.site.root),
The prefix doesn't matter -- you can use ``databrowse/`` or ``db/`` or
whatever you'd like.
4. Run the Django server and visit ``/databrowse/`` in your browser.
Requiring user login
====================
You can restrict access to logged-in users with only a few extra lines of
code. Simply add the following import to your URLconf::
from django.contrib.auth.decorators import login_required
Then modify the :ref:`URLconf <topics-http-urls>` so that the
:func:`databrowse.site.root` view is decorated with
:func:`django.contrib.auth.decorators.login_required`::
(r'^databrowse/(.*)', login_required(databrowse.site.root)),
If you haven't already added support for user logins to your :ref:`URLconf
<topics-http-urls>`, as described in the :ref:`user authentication docs
<ref-contrib-auth>`, then you will need to do so now with the following
mapping::
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
The final step is to create the login form required by
:func:`django.contrib.auth.views.login`. The
:ref:`user authentication docs <ref-contrib-auth>` provide full details and a
sample template that can be used for this purpose.

View file

@ -0,0 +1,153 @@
.. _ref-contrib-flatpages:
=================
The flatpages app
=================
.. module:: django.contrib.flatpages
:synopsis: A framework for managing simple ?flat? HTML content in a database.
Django comes with an optional "flatpages" application. It lets you store simple
"flat" HTML content in a database and handles the management for you via
Django's admin interface and a Python API.
A flatpage is a simple object with a URL, title and content. Use it for
one-off, special-case pages, such as "About" or "Privacy Policy" pages, that
you want to store in a database but for which you don't want to develop a
custom Django application.
A flatpage can use a custom template or a default, systemwide flatpage
template. It can be associated with one, or multiple, sites.
**New in Django development version**
The content field may optionally be left blank if you prefer to put your
content in a custom template.
Here are some examples of flatpages on Django-powered sites:
* http://www.chicagocrime.org/about/
* http://www.everyblock.com/about/
* http://www.lawrence.com/about/contact/
Installation
============
To install the flatpages app, follow these steps:
1. Install the :mod:`sites framework <django.contrib.sites>` by adding
``'django.contrib.sites'`` to your :setting:`INSTALLED_APPS` setting,
if it's not already in there.
2. Add ``'django.contrib.flatpages'`` to your :setting:`INSTALLED_APPS`
setting.
3. Add ``'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting.
4. Run the command :djadmin:`manage.py syncdb <syncdb>`.
How it works
============
``manage.py syncdb`` creates two tables in your database: ``django_flatpage``
and ``django_flatpage_sites``. ``django_flatpage`` is a simple lookup table
that simply maps a URL to a title and bunch of text content.
``django_flatpage_sites`` associates a flatpage with a site.
The :class:`~django.contrib.flatpages.middleware.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 :setting:`SITE_ID` setting.
If it finds a match, it follows this algorithm:
* If the flatpage has a custom template, it loads that template. Otherwise,
it loads the template :file:`flatpages/default.html`.
* It passes that template a single context variable, :data:`flatpage`, which
is the flatpage object. It uses
:class:`~django.template.context.RequestContext` in rendering the
template.
If it doesn't find a match, the request continues to be processed as usual.
The middleware only gets activated for 404s -- not for 500s or responses of any
other status code.
Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you can
put :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` at
the end of the list, because it's a last resort.
For more on middleware, read the :ref:`middleware docs
<topics-http-middleware>`.
.. admonition:: Ensure that your 404 template works
Note that the
:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
only steps in once another view has successfully produced a 404 response.
If another view or middleware class attempts to produce a 404 but ends up
raising an exception instead (such as a ``TemplateDoesNotExist``
exception if your site does not have an appropriate template to
use for HTTP 404 responses), the response will become an HTTP 500
("Internal Server Error") and the
:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
will not attempt to serve a flat page.
How to add, change and delete flatpages
=======================================
Via the admin interface
-----------------------
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.
Via the Python API
------------------
.. class:: models.FlatPage
Flatpages are represented by a standard
:ref:`Django model <topics-db-models>`,
which lives in `django/contrib/flatpages/models.py`_. You can access
flatpage objects via the :ref:`Django database API <topics-db-queries>`.
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
Flatpage templates
==================
By default, flatpages are rendered via the template
:file:`flatpages/default.html`, but you can override that for a particular
flatpage.
Creating the :file:`flatpages/default.html` template is your responsibility;
in your template directory, just create a :file:`flatpages` directory
containing a file :file:`default.html`.
Flatpage templates are passed a single context variable, :data:`flatpage`,
which is the flatpage object.
Here's a sample :file:`flatpages/default.html` template:
.. code-block:: html+django
<!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>
Since you're already entering raw HTML into the admin page for a flatpage,
both ``flatpage.title`` and ``flatpage.content`` are marked as **not**
requiring :ref:`automatic HTML escaping <automatic-html-escaping>` in the
template.

View file

@ -0,0 +1,110 @@
.. _ref-contrib-formtools-form-preview:
============
Form preview
============
.. module:: django.contrib.formtools
:synopsis: Displays an HTML form, forces a preview, then does something
with the submission.
Django comes with an optional "form preview" application that helps automate
the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
To force a preview of a form submission, all you have to do is write a short
Python class.
Overview
=========
Given a :class:`django.forms.Form` subclass that you define, this
application takes care of the following workflow:
1. Displays the form as HTML on a Web page.
2. Validates the form data when it's submitted via POST.
a. If it's valid, displays a preview page.
b. If it's not valid, redisplays the form with error messages.
3. When the "confirmation" form is submitted from the preview page, calls
a hook that you define -- a
:meth:`~django.contrib.formtools.FormPreview.done()` method that gets
passed the valid data.
The framework enforces the required preview by passing a shared-secret hash to
the preview page via hidden form fields. If somebody tweaks the form parameters
on the preview page, the form submission will fail the hash-comparison test.
How to use ``FormPreview``
==========================
1. Point Django at the default FormPreview templates. There are two ways to
do this:
* Add ``'django.contrib.formtools'`` to your
:setting:`INSTALLED_APPS` setting. This will work if your
:setting:`TEMPLATE_LOADERS` setting includes the
``app_directories`` template loader (which is the case by
default). See the :ref:`template loader docs <template-loaders>`
for more.
* Otherwise, determine the full filesystem path to the
:file:`django/contrib/formtools/templates` directory, and add that
directory to your :setting:`TEMPLATE_DIRS` setting.
2. Create a :class:`~django.contrib.formtools.FormPreview` subclass that
overrides the :meth:`~django.contrib.formtools.FormPreview.done()`
method::
from django.contrib.formtools.preview import FormPreview
from myapp.models import SomeModel
class SomeModelFormPreview(FormPreview):
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/form/success')
This method takes an :class:`~django.http.HttpRequest` object and a
dictionary of the form data after it has been validated and cleaned.
It should return an :class:`~django.http.HttpResponseRedirect` that
is the end result of the form being submitted.
3. Change your URLconf to point to an instance of your
:class:`~django.contrib.formtools.FormPreview` subclass::
from myapp.preview import SomeModelFormPreview
from myapp.models import SomeModel
from django import forms
...and add the following line to the appropriate model in your URLconf::
(r'^post/$', SomeModelFormPreview(SomeModelForm)),
where ``SomeModelForm`` is a Form or ModelForm class for the model.
4. Run the Django server and visit :file:`/post/` in your browser.
``FormPreview`` classes
=======================
.. class:: FormPreview
A :class:`~django.contrib.formtools.FormPreview` class is a simple Python class
that represents the preview workflow.
:class:`~django.contrib.formtools.FormPreview` classes must subclass
``django.contrib.formtools.preview.FormPreview`` and override the
:meth:`~django.contrib.formtools.FormPreview.done()` method. They can live
anywhere in your codebase.
``FormPreview`` templates
=========================
By default, the form is rendered via the template :file:`formtools/form.html`,
and the preview page is rendered via the template :file:`formtools.preview.html`.
These values can be overridden for a particular form preview by setting
:attr:`~django.contrib.formtools.FormPreview.preview_template` and
:attr:`~django.contrib.formtools.FormPreview.form_template` attributes on the
FormPreview subclass. See :file:`django/contrib/formtools/templates` for the
default templates.

View file

@ -0,0 +1,316 @@
.. _ref-contrib-formtools-form-wizard:
===========
Form wizard
===========
.. module:: django.contrib.formtools.wizard
:synopsis: Splits forms across multiple Web pages.
**New in Django development version.**
Django comes with an optional "form wizard" application that splits
:ref:`forms <topics-forms-index>` across multiple Web pages. It maintains
state in hashed HTML :samp:`<input type="hidden">` fields, and the data isn't
processed server-side until the final form is submitted.
You might want to use this if you have a lengthy form that would be too
unwieldy for display on a single page. The first page might ask the user for
core information, the second page might ask for less important information,
etc.
The term "wizard," in this context, is `explained on Wikipedia`_.
.. _explained on Wikipedia: http://en.wikipedia.org/wiki/Wizard_%28software%29
.. _forms: ../forms/
How it works
============
Here's the basic workflow for how a user would use a wizard:
1. The user visits the first page of the wizard, fills in the form and
submits it.
2. The server validates the data. If it's invalid, the form is displayed
again, with error messages. If it's valid, the server calculates a
secure hash of the data and presents the user with the next form,
saving the validated data and hash in :samp:`<input type="hidden">`
fields.
3. Step 1 and 2 repeat, for every subsequent form in the wizard.
4. Once the user has submitted all the forms and all the data has been
validated, the wizard processes the data -- saving it to the database,
sending an e-mail, or whatever the application needs to do.
Usage
=====
This application handles as much machinery for you as possible. Generally, you
just have to do these things:
1. Define a number of :mod:`django.forms`
:class:`~django.forms.forms.Form` classes -- one per wizard page.
2. Create a :class:`~django.contrib.formtools.wizard.FormWizard` class
that specifies what to do once all of your forms have been submitted
and validated. This also lets you override some of the wizard's behavior.
3. Create some templates that render the forms. You can define a single,
generic template to handle every one of the forms, or you can define a
specific template for each form.
4. Point your URLconf at your
:class:`~django.contrib.formtools.wizard.FormWizard` class.
Defining ``Form`` classes
=========================
The first step in creating a form wizard is to create the :class:`~django.forms.forms.Form` classes.
These should be standard :mod:`django.forms`
:class:`~django.forms.forms.Form` classes, covered in the
:ref:`forms documentation <topics-forms-index>`.
These classes can live anywhere in your codebase, but convention is to put them
in a file called :file:`forms.py` in your application.
For example, let's write a "contact form" wizard, where the first page's form
collects the sender's e-mail address and subject, and the second page collects
the message itself. Here's what the :file:`forms.py` might look like::
from django import forms
class ContactForm1(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.EmailField()
class ContactForm2(forms.Form):
message = forms.CharField(widget=forms.Textarea)
**Important limitation:** Because the wizard uses HTML hidden fields to store
data between pages, you may not include a :class:`~django.forms.fields.FileField`
in any form except the last one.
Creating a ``FormWizard`` class
===============================
The next step is to create a :class:`~django.contrib.formtools.wizard.FormWizard`
class, which should be a subclass of ``django.contrib.formtools.wizard.FormWizard``.
As your :class:`~django.forms.forms.Form` classes, this
:class:`~django.contrib.formtools.wizard.FormWizard` class can live anywhere
in your codebase, but convention is to put it in :file:`forms.py`.
The only requirement on this subclass is that it implement a
:meth:`~django.contrib.formtools.wizard.FormWizard.done()` method,
which specifies what should happen when the data for *every* form is submitted
and validated. This method is passed two arguments:
* ``request`` -- an :class:`~django.http.HttpRequest` object
* ``form_list`` -- a list of :mod:`django.forms`
:class:`~django.forms.forms.Form` classes
In this simplistic example, rather than perform any database operation, the
method simply renders a template of the validated data::
from django.shortcuts import render_to_response
from django.contrib.formtools.wizard import FormWizard
class ContactWizard(FormWizard):
def done(self, request, form_list):
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
Note that this method will be called via ``POST``, so it really ought to be a
good Web citizen and redirect after processing the data. Here's another
example::
from django.http import HttpResponseRedirect
from django.contrib.formtools.wizard import FormWizard
class ContactWizard(FormWizard):
def done(self, request, form_list):
do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/page-to-redirect-to-when-done/')
See the section `Advanced FormWizard methods`_ below to learn about more
:class:`~django.contrib.formtools.wizard.FormWizard` hooks.
Creating templates for the forms
================================
Next, you'll need to create a template that renders the wizard's forms. By
default, every form uses a template called :file:`forms/wizard.html`. (You can
change this template name by overriding
:meth:`~django.contrib.formtools.wizard..get_template()`, which is documented
below. This hook also allows you to use a different template for each form.)
This template expects the following context:
* ``step_field`` -- The name of the hidden field containing the step.
* ``step0`` -- The current step (zero-based).
* ``step`` -- The current step (one-based).
* ``step_count`` -- The total number of steps.
* ``form`` -- The :class:`~django.forms.forms.Form` instance for the
current step (either empty or with errors).
* ``previous_fields`` -- A string representing every previous data field,
plus hashes for completed forms, all in the form of hidden fields. Note
that you'll need to run this through the
:meth:`~django.template.defaultfilters.safe` template filter, to prevent
auto-escaping, because it's raw HTML.
It will also be passed any objects in :data:`extra_context`, which is a
dictionary you can specify that contains extra values to add to the context.
You can specify it in two ways:
* Set the :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context`
attribute on your :class:`~django.contrib.formtools.wizard.FormWizard`
subclass to a dictionary.
* Pass :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context`
as extra parameters in the URLconf.
Here's a full example template::
{% extends "base.html" %}
{% block content %}
<p>Step {{ step }} of {{ step_count }}</p>
<form action="." method="post">
<table>
{{ form }}
</table>
<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />
{{ previous_fields|safe }}
<input type="submit">
</form>
{% endblock %}
Note that ``previous_fields``, ``step_field`` and ``step0`` are all required
for the wizard to work properly.
Hooking the wizard into a URLconf
=================================
Finally, give your new :class:`~django.contrib.formtools.wizard.FormWizard`
object a URL in ``urls.py``. The wizard takes a list of your form objects as
arguments::
from django.conf.urls.defaults import *
from mysite.testapp.forms import ContactForm1, ContactForm2, ContactWizard
urlpatterns = patterns('',
(r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
)
Advanced FormWizard methods
===========================
.. class:: FormWizard
Aside from the :meth:`~django.contrib.formtools.wizard.FormWizard.done()`
method, :class:`~django.contrib.formtools.wizard.FormWizard` offers a few
advanced method hooks that let you customize how your wizard works.
Some of these methods take an argument ``step``, which is a zero-based counter
representing the current step of the wizard. (E.g., the first form is ``0`` and
the second form is ``1``.)
.. method:: FormWizard.prefix_for_step
Given the step, returns a :class:`~django.forms.forms.Form` prefix to
use. By default, this simply uses the step itself. For more, see the
:ref:`form prefix documentation <form-prefix>`.
Default implementation::
def prefix_for_step(self, step):
return str(step)
.. method:: FormWizard.render_hash_failure
Renders a template if the hash check fails. It's rare that you'd need to
override this.
Default implementation::
def render_hash_failure(self, request, step):
return self.render(self.get_form(step), request, step,
context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'})
.. method:: FormWizard.security_hash
Calculates the security hash for the given request object and :class:`~django.forms.forms.Form` instance.
By default, this uses an MD5 hash of the form data and your
:setting:`SECRET_KEY` setting. It's rare that somebody would need to override
this.
Example::
def security_hash(self, request, form):
return my_hash_function(request, form)
.. method:: FormWizard.parse_params
A hook for saving state from the request object and ``args`` / ``kwargs`` that
were captured from the URL by your URLconf.
By default, this does nothing.
Example::
def parse_params(self, request, *args, **kwargs):
self.my_state = args[0]
.. method:: FormWizard.get_template
Returns the name of the template that should be used for the given step.
By default, this returns :file:`'forms/wizard.html'`, regardless of step.
Example::
def get_template(self, step):
return 'myapp/wizard_%s.html' % step
If :meth:`~FormWizard.get_template` returns a list of strings, then the wizard will use the
template system's :func:`~django.template.loader.select_template()`
function,
:ref:`explained in the template docs <ref-templates-api-the-python-api>`.
This means the system will use the first template that exists on the
filesystem. For example::
def get_template(self, step):
return ['myapp/wizard_%s.html' % step, 'myapp/wizard.html']
.. _explained in the template docs: ../templates_python/#the-python-api
.. method:: FormWizard.render_template
Renders the template for the given step, returning an
:class:`~django.http.HttpResponseRedirect` object.
Override this method if you want to add a custom context, return a different
MIME type, etc. If you only need to override the template name, use
:meth:`~FormWizard.get_template` instead.
The template will be rendered with the context documented in the
"Creating templates for the forms" section above.
.. method:: FormWizard.process_step
Hook for modifying the wizard's internal state, given a fully validated
:class:`~django.forms.forms.Form` object. The Form is guaranteed to
have clean, valid data.
This method should *not* modify any of that data. Rather, it might want to set
``self.extra_context`` or dynamically alter ``self.form_list``, based on
previously submitted forms.
Note that this method is called every time a page is rendered for *all*
submitted steps.
The function signature::
def process_step(self, request, form, step):
# ...

View file

@ -0,0 +1,12 @@
.. _ref-contrib-formtools-index:
django.contrib.formtools
========================
A set of high-level abstractions for Django forms (:mod:`django.forms`).
.. toctree::
:maxdepth: 1
form-preview
form-wizard

View file

@ -0,0 +1,91 @@
.. _ref-contrib-humanize:
========================
django.contrib.humanize
========================
.. module:: django.contrib.humanize
:synopsis: A set of Django template filters useful for adding a "human
touch" to data.
A set of Django template filters useful for adding a "human touch" to data.
To activate these filters, add ``'django.contrib.humanize'`` to your
:setting:`INSTALLED_APPS` setting. Once you've done that, use
``{% load humanize %}`` in a template, and you'll have access to these filters:
apnumber
--------
For numbers 1-9, returns the number spelled out. Otherwise, returns the
number. This follows Associated Press style.
Examples:
* ``1`` becomes ``'one'``.
* ``2`` becomes ``'two'``.
* ``10`` becomes ``10``.
You can pass in either an integer or a string representation of an integer.
intcomma
--------
Converts an integer to a string containing commas every three digits.
Examples:
* ``4500`` becomes ``'4,500'``.
* ``45000`` becomes ``'45,000'``.
* ``450000`` becomes ``'450,000'``.
* ``4500000`` becomes ``'4,500,000'``.
You can pass in either an integer or a string representation of an integer.
intword
-------
Converts a large integer to a friendly text representation. Works best for
numbers over 1 million.
Examples:
* ``1000000`` becomes ``'1.0 million'``.
* ``1200000`` becomes ``'1.2 million'``.
* ``1200000000`` becomes ``'1.2 billion'``.
Values up to 1000000000000000 (one quadrillion) are supported.
You can pass in either an integer or a string representation of an integer.
ordinal
-------
Converts an integer to its ordinal as a string.
Examples:
* ``1`` becomes ``'1st'``.
* ``2`` becomes ``'2nd'``.
* ``3`` becomes ``'3rd'``.
You can pass in either an integer or a string representation of an integer.
naturalday
----------
**New in Django development version**
For dates that are the current day or within one day, return "today",
"tomorrow" or "yesterday", as appropriate. Otherwise, format the date using
the passed in format string.
**Argument:** Date formatting string as described in the :ttag:`now` tag.
Examples (when 'today' is 17 Feb 2007):
* ``16 Feb 2007`` becomes ``yesterday``.
* ``17 Feb 2007`` becomes ``today``.
* ``18 Feb 2007`` becomes ``tomorrow``.
* Any other day is formatted according to given argument or the
:setting:`DATE_FORMAT` setting if no argument is given.

199
docs/ref/contrib/index.txt Normal file
View file

@ -0,0 +1,199 @@
.. _ref-contrib-index:
============================
The "django.contrib" add-ons
============================
Django aims to follow Python's `"batteries included" philosophy`_. It ships
with a variety of extra, optional tools that solve common Web-development
problems.
This code lives in ``django/contrib`` in the Django distribution. This document
gives a rundown of the packages in ``contrib``, along with any dependencies
those packages have.
.. admonition:: Note
For most of these add-ons -- specifically, the add-ons that include either
models or template tags -- you'll need to add the package name (e.g.,
``'django.contrib.admin'``) to your ``INSTALLED_APPS`` setting and re-run
``manage.py syncdb``.
.. _"batteries included" philosophy: http://docs.python.org/tut/node12.html#batteries-included
.. toctree::
:maxdepth: 1
admin
auth
contenttypes
csrf
databrowse
flatpages
formtools/index
humanize
localflavor
redirects
sitemaps
sites
syndication
webdesign
admin
=====
The automatic Django administrative interface. For more information, see
:ref:`Tutorial 2 <intro-tutorial02>` and the
:ref:`admin documentation <ref-contrib-admin>`.
Requires the auth_ and contenttypes_ contrib packages to be installed.
auth
====
Django's authentication framework.
See :ref:`topics-auth`.
comments
========
A simple yet flexible comments system. This is not yet documented.
contenttypes
============
A light framework for hooking into "types" of content, where each installed
Django model is a separate content type.
See the :ref:`contenttypes documentation <ref-contrib-contenttypes>`.
csrf
====
A middleware for preventing Cross Site Request Forgeries
See the :ref:`csrf documentation <ref-contrib-csrf>`.
flatpages
=========
A framework for managing simple "flat" HTML content in a database.
See the :ref:`flatpages documentation <ref-contrib-flatpages>`.
Requires the sites_ contrib package to be installed as well.
formtools
=========
A set of high-level abstractions for Django forms (django.forms).
django.contrib.formtools.preview
--------------------------------
An abstraction of the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
See the :ref:`form preview documentation <ref-contrib-formtools-form-preview>`.
django.contrib.formtools.wizard
--------------------------------
Splits forms across multiple Web pages.
See the :ref:`form wizard documentation <ref-contrib-formtools-form-wizard>`.
humanize
========
A set of Django template filters useful for adding a "human touch" to data.
See the :ref:`humanize documentation <ref-contrib-humanize>`.
localflavor
===========
A collection of various Django snippets that are useful only for a particular
country or culture. For example, ``django.contrib.localflavor.us.forms``
contains a ``USZipCodeField`` that you can use to validate U.S. zip codes.
See the :ref:`localflavor documentation <ref-contrib-localflavor>`.
.. _ref-contrib-markup:
markup
======
A collection of template filters that implement common markup languages:
* ``textile`` -- implements `Textile`_
* ``markdown`` -- implements `Markdown`_
* ``restructuredtext`` -- implements `ReST (ReStructured Text)`_
In each case, the filter expects formatted markup as a string and returns a
string representing the marked-up text. For example, the ``textile`` filter
converts text that is marked-up in Textile format to HTML.
To activate these filters, add ``'django.contrib.markup'`` to your
:setting:`INSTALLED_APPS` setting. Once you've done that, use ``{% load markup %}`` in
a template, and you'll have access to these filters. For more documentation,
read the source code in django/contrib/markup/templatetags/markup.py.
.. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29
.. _Markdown: http://en.wikipedia.org/wiki/Markdown
.. _ReST (ReStructured Text): http://en.wikipedia.org/wiki/ReStructuredText
redirects
=========
A framework for managing redirects.
See the :ref:`redirects documentation <ref-contrib-redirects>`.
sessions
========
A framework for storing data in anonymous sessions.
See the :ref:`sessions documentation <topics-http-sessions>`.
sites
=====
A light framework that lets you operate multiple Web sites off of the same
database and Django installation. It gives you hooks for associating objects to
one or more sites.
See the :ref:`sites documentation <ref-contrib-sites>`.
sitemaps
========
A framework for generating Google sitemap XML files.
See the :ref:`sitemaps documentation <ref-contrib-sitemaps>`.
syndication
===========
A framework for generating syndication feeds, in RSS and Atom, quite easily.
See the :ref:`syndication documentation <ref-contrib-syndication>`.
webdesign
=========
Helpers and utilities targeted primarily at Web *designers* rather than
Web *developers*.
See the :ref:`Web design helpers documentation <ref-contrib-webdesign>`.
Other add-ons
=============
If you have an idea for functionality to include in ``contrib``, let us know!
Code it up, and post it to the `django-users mailing list`_.
.. _django-users mailing list: http://groups.google.com/group/django-users

View file

@ -0,0 +1,651 @@
.. _ref-contrib-localflavor:
==========================
The "local flavor" add-ons
==========================
.. module:: django.contrib.localflavor
:synopsis: A collection of various Django snippets that are useful only for
a particular country or culture.
Following its "batteries included" philosophy, Django comes with assorted
pieces of code that are useful for particular countries or cultures. These are
called the "local flavor" add-ons and live in the
:mod:`django.contrib.localflavor` package.
Inside that package, country- or culture-specific code is organized into
subpackages, named using `ISO 3166 country codes`_.
Most of the ``localflavor`` add-ons are localized form components deriving
from the :ref:`forms <topics-forms-index>` framework -- for example, a
:class:`~django.contrib.localflavor.us.forms.USStateField` that knows how to
validate U.S. state abbreviations, and a
:class:`~django.contrib.localflavor.fi.forms.FISocialSecurityNumber` that
knows how to validate Finnish social security numbers.
To use one of these localized components, just import the relevant subpackage.
For example, here's how you can create a form with a field representing a
French telephone number::
from django import forms
from django.contrib.localflavor import fr
class MyForm(forms.Form):
my_french_phone_no = fr.forms.FRPhoneNumberField()
Supported countries
===================
Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Argentina_
* Australia_
* Austria_
* Brazil_
* Canada_
* Chile_
* Finland_
* France_
* Germany_
* Holland_
* Iceland_
* India_
* Italy_
* Japan_
* Mexico_
* Norway_
* Peru_
* Poland_
* Romania_
* Slovakia_
* `South Africa`_
* Spain_
* Switzerland_
* `United Kingdom`_
* `United States of America`_
The ``django.contrib.localflavor`` package also includes a ``generic`` subpackage,
containing useful code that is not specific to one particular country or
culture. Currently, it defines date and datetime input fields based on those
from :ref:`forms <topics-forms-index>`, but with non-US default formats.
Here's an example of how to use them::
from django import forms
from django.contrib.localflavor import generic
class MyForm(forms.Form):
my_date_field = generic.forms.DateField()
.. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
.. _Argentina: `Argentina (ar)`_
.. _Australia: `Australia (au)`_
.. _Austria: `Austria (at)`_
.. _Brazil: `Brazil (br)`_
.. _Canada: `Canada (ca)`_
.. _Chile: `Chile (cl)`_
.. _Finland: `Finland (fi)`_
.. _France: `France (fr)`_
.. _Germany: `Germany (de)`_
.. _Holland: `Holland (nl)`_
.. _Iceland: `Iceland (is\_)`_
.. _India: `India (in\_)`_
.. _Italy: `Italy (it)`_
.. _Japan: `Japan (jp)`_
.. _Mexico: `Mexico (mx)`_
.. _Norway: `Norway (no)`_
.. _Peru: `Peru (pe)`_
.. _Poland: `Poland (pl)`_
.. _Romania: `Romania (ro)`_
.. _Slovakia: `Slovakia (sk)`_
.. _South Africa: `South Africa (za)`_
.. _Spain: `Spain (es)`_
.. _Switzerland: `Switzerland (ch)`_
.. _United Kingdom: `United Kingdom (uk)`_
.. _United States of America: `United States of America (us)`_
Adding flavors
==============
We'd love to add more of these to Django, so please `create a ticket`_ with
any code you'd like to contribute. One thing we ask is that you please use
Unicode objects (``u'mystring'``) for strings, rather than setting the encoding
in the file. See any of the existing flavors for examples.
.. _create a ticket: http://code.djangoproject.com/simpleticket
Argentina (``ar``)
=============================================
.. class:: ar.forms.ARPostalCodeField
A form field that validates input as either a classic four-digit Argentinian
postal code or a CPA_.
.. _CPA: http://www.correoargentino.com.ar/consulta_cpa/home.php
.. class:: ar.forms.ARDNIField
A form field that validates input as a Documento Nacional de Identidad (DNI)
number.
.. class:: ar.forms.ARCUITField
A form field that validates input as a Codigo Unico de Identificacion
Tributaria (CUIT) number.
.. class:: ar.forms.ARProvinceSelect
A ``Select`` widget that uses a list of Argentina's provinces and autonomous
cities as its choices.
Australia (``au``)
=============================================
.. class:: au.forms.AUPostCodeField
A form field that validates input as an Australian postcode.
.. class:: au.forms.AUPhoneNumberField
A form field that validates input as an Australian phone number. Valid numbers
have ten digits.
.. class:: au.forms.AUStateSelect
A ``Select`` widget that uses a list of Australian states/territories as its
choices.
Austria (``at``)
================
.. class:: at.forms.ATZipCodeField
A form field that validates its input as an Austrian zip code.
.. class:: at.forms.ATStateSelect
A ``Select`` widget that uses a list of Austrian states as its choices.
.. class:: at.forms.ATSocialSecurityNumberField
A form field that validates its input as an Austrian social security number.
Brazil (``br``)
===============
.. class:: br.forms.BRPhoneNumberField
A form field that validates input as a Brazilian phone number, with the format
XX-XXXX-XXXX.
.. class:: br.forms.BRZipCodeField
A form field that validates input as a Brazilian zip code, with the format
XXXXX-XXX.
.. class:: br.forms.BRStateSelect
A ``Select`` widget that uses a list of Brazilian states/territories as its
choices.
Canada (``ca``)
===============
.. class:: ca.forms.CAPhoneNumberField
A form field that validates input as a Canadian phone number, with the format
XXX-XXX-XXXX.
.. class:: ca.forms.CAPostalCodeField
A form field that validates input as a Canadian postal code, with the format
XXX XXX.
.. class:: ca.forms.CAProvinceField
A form field that validates input as a Canadian province name or abbreviation.
.. class:: ca.forms.CASocialInsuranceNumberField
A form field that validates input as a Canadian Social Insurance Number (SIN).
A valid number must have the format XXX-XXX-XXX and pass a `Luhn mod-10
checksum`_.
.. _Luhn mod-10 checksum: http://en.wikipedia.org/wiki/Luhn_algorithm
.. class:: ca.forms.CAProvinceSelect
A ``Select`` widget that uses a list of Canadian provinces and territories as
its choices.
Chile (``cl``)
==============
.. class:: cl.forms.CLRutField
A form field that validates input as a Chilean national identification number
('Rol Unico Tributario' or RUT). The valid format is XX.XXX.XXX-X.
.. class:: cl.forms.CLRegionSelect
A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
choices.
Finland (``fi``)
================
.. class:: fi.forms.FISocialSecurityNumber
A form field that validates input as a Finnish social security number.
.. class:: fi.forms.FIZipCodeField
A form field that validates input as a Finnish zip code. Valid codes
consist of five digits.
.. class:: fi.forms.FIMunicipalitySelect
A ``Select`` widget that uses a list of Finnish municipalities as its
choices.
France (``fr``)
===============
.. class:: fr.forms.FRPhoneNumberField
A form field that validates input as a French local phone number. The
correct format is 0X XX XX XX XX. 0X.XX.XX.XX.XX and 0XXXXXXXXX validate
but are corrected to 0X XX XX XX XX.
.. class:: fr.forms.FRZipCodeField
A form field that validates input as a French zip code. Valid codes
consist of five digits.
.. class:: fr.forms.FRDepartmentSelect
A ``Select`` widget that uses a list of French departments as its choices.
Germany (``de``)
================
.. class:: de.forms.DEIdentityCardNumberField
A form field that validates input as a German identity card number
(Personalausweis_). Valid numbers have the format
XXXXXXXXXXX-XXXXXXX-XXXXXXX-X, with no group consisting entirely of zeroes.
.. _Personalausweis: http://de.wikipedia.org/wiki/Personalausweis
.. class:: de.forms.DEZipCodeField
A form field that validates input as a German zip code. Valid codes
consist of five digits.
.. class:: de.forms.DEStateSelect
A ``Select`` widget that uses a list of German states as its choices.
Holland (``nl``)
================
.. class:: nl.forms.NLPhoneNumberField
A form field that validates input as a Dutch telephone number.
.. class:: nl.forms.NLSofiNumberField
A form field that validates input as a Dutch social security number
(SoFI/BSN).
.. class:: nl.forms.NLZipCodeField
A form field that validates input as a Dutch zip code.
.. class:: nl.forms.NLProvinceSelect
A ``Select`` widget that uses a list of Dutch provinces as its list of
choices.
Iceland (``is_``)
=================
.. class:: is_.forms.ISIdNumberField
A form field that validates input as an Icelandic identification number
(kennitala). The format is XXXXXX-XXXX.
.. class:: is_.forms.ISPhoneNumberField
A form field that validates input as an Icelandtic phone number (seven
digits with an optional hyphen or space after the first three digits).
.. class:: is_.forms.ISPostalCodeSelect
A ``Select`` widget that uses a list of Icelandic postal codes as its
choices.
India (``in_``)
===============
.. class:: in.forms.INStateField
A form field that validates input as an Indian state/territory name or
abbreviation. Input is normalized to the standard two-letter vehicle
registration abbreviation for the given state or territory.
.. class:: in.forms.INZipCodeField
A form field that validates input as an Indian zip code, with the
format XXXXXXX.
.. class:: in.forms.INStateSelect
A ``Select`` widget that uses a list of Indian states/territories as its
choices.
Italy (``it``)
==============
.. class:: it.forms.ITSocialSecurityNumberField
A form field that validates input as an Italian social security number
(`codice fiscale`_).
.. _codice fiscale: http://www.agenziaentrate.it/ilwwcm/connect/Nsi/Servizi/Codice+fiscale+-+tessera+sanitaria/Codice+fiscale/NSI+Informazioni+sulla+codificazione+delle+persone+fisiche
.. class:: it.forms.ITVatNumberField
A form field that validates Italian VAT numbers (partita IVA).
.. class:: it.forms.ITZipCodeField
A form field that validates input as an Italian zip code. Valid codes
must have five digits.
.. class:: it.forms.ITProvinceSelect
A ``Select`` widget that uses a list of Italian provinces as its choices.
.. class:: it.forms.ITRegionSelect
A ``Select`` widget that uses a list of Italian regions as its choices.
Japan (``jp``)
==============
.. class:: jp.forms.JPPostalCodeField
A form field that validates input as a Japanese postcode. It accepts seven
digits, with or without a hyphen.
.. class:: jp.forms.JPPrefectureSelect
A ``Select`` widget that uses a list of Japanese prefectures as its choices.
Mexico (``mx``)
===============
.. class:: mx.forms.MXStateSelect
A ``Select`` widget that uses a list of Mexican states as its choices.
Norway (``no``)
===============
.. class:: no.forms.NOSocialSecurityNumber
A form field that validates input as a Norwegian social security number
(personnummer_).
.. _personnummer: http://no.wikipedia.org/wiki/Personnummer
.. class:: no.forms.NOZipCodeField
A form field that validates input as a Norwegian zip code. Valid codes
have four digits.
.. class:: no.forms.NOMunicipalitySelect
A ``Select`` widget that uses a list of Norwegian municipalities (fylker) as
its choices.
Peru (``pe``)
=============
.. class:: pt.forms.PEDNIField
A form field that validates input as a DNI (Peruvian national identity)
number.
.. class:: pt.forms.PERUCField
A form field that validates input as an RUC (Registro Unico de
Contribuyentes) number. Valid RUC numbers have 11 digits.
.. class:: pt.forms.PEDepartmentSelect
A ``Select`` widget that uses a list of Peruvian Departments as its choices.
Poland (``pl``)
===============
.. class:: pl.forms.PLNationalIdentificationNumberField
A form field that validates input as a Polish national identification number
(PESEL_).
.. _PESEL: http://en.wikipedia.org/wiki/PESEL
.. class:: pl.forms.PLNationalBusinessRegisterField
A form field that validates input as a Polish National Official Business
Register Number (REGON_), having either seven or nine digits. The checksum
algorithm used for REGONs is documented at
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
.. _REGON: http://www.stat.gov.pl/bip/regon_ENG_HTML.htm
.. class:: pl.forms.PLPostalCodeField
A form field that validates input as a Polish postal code. The valid format
is XX-XXX, where X is a digit.
.. class:: pl.forms.PLTaxNumberField
A form field that validates input as a Polish Tax Number (NIP). Valid
formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
for NIPs is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
.. class:: pl.forms.PLAdministrativeUnitSelect
A ``Select`` widget that uses a list of Polish administrative units as its
choices.
.. class:: pl.forms.PLVoivodeshipSelect
A ``Select`` widget that uses a list of Polish voivodeships (administrative
provinces) as its choices.
Romania (``ro``)
================
.. class:: ro.forms.ROCIFField
A form field that validates Romanian fiscal identification codes (CIF). The
return value strips the leading RO, if given.
.. class:: ro.forms.ROCNPField
A form field that validates Romanian personal numeric codes (CNP).
.. class:: ro.forms.ROCountyField
A form field that validates its input as a Romanian county (judet) name or
abbreviation. It normalizes the input to the standard vehicle registration
abbreviation for the given county. This field will only accept names written
with diacritics; consider using ROCountySelect as an alternative.
.. class:: ro.forms.ROCountySelect
A ``Select`` widget that uses a list of Romanian counties (judete) as its
choices.
.. class:: ro.forms.ROIBANField
A form field that validates its input as a Romanian International Bank
Account Number (IBAN). The valid format is ROXX-XXXX-XXXX-XXXX-XXXX-XXXX,
with or without hyphens.
.. class:: ro.forms.ROPhoneNumberField
A form field that validates Romanian phone numbers, short special numbers
excluded.
.. class:: ro.forms.ROPostalCodeField
A form field that validates Romanian postal codes.
Slovakia (``sk``)
=================
.. class:: sk.forms.SKPostalCodeField
A form field that validates input as a Slovak postal code. Valid formats
are XXXXX or XXX XX, where X is a digit.
.. class:: sk.forms.SKDistrictSelect
A ``Select`` widget that uses a list of Slovak districts as its choices.
.. class:: sk.forms.SKRegionSelect
A ``Select`` widget that uses a list of Slovak regions as its choices.
South Africa (``za``)
=====================
.. class:: za.forms.ZAIDField
A form field that validates input as a South African ID number. Validation
uses the Luhn checksum and a simplistic (i.e., not entirely accurate) check
for birth date.
.. class:: za.forms.ZAPostCodeField
A form field that validates input as a South African postcode. Valid
postcodes must have four digits.
Spain (``es``)
==============
.. class:: es.forms.ESIdentityCardNumberField
A form field that validates input as a Spanish NIF/NIE/CIF (Fiscal
Identification Number) code.
.. class:: es.forms.ESCCCField
A form field that validates input as a Spanish bank account number (Codigo
Cuenta Cliente or CCC). A valid CCC number has the format
EEEE-OOOO-CC-AAAAAAAAAA, where the E, O, C and A digits denote the entity,
office, checksum and account, respectively. The first checksum digit
validates the entity and office. The second checksum digit validates the
account. It is also valid to use a space as a delimiter, or to use no
delimiter.
.. class:: es.forms.ESPhoneNumberField
A form field that validates input as a Spanish phone number. Valid numbers
have nine digits, the first of which is 6, 8 or 9.
.. class:: es.forms.ESPostalCodeField
A form field that validates input as a Spanish postal code. Valid codes
have five digits, the first two being in the range 01 to 52, representing
the province.
.. class:: es.forms.ESProvinceSelect
A ``Select`` widget that uses a list of Spanish provinces as its choices.
.. class:: es.forms.ESRegionSelect
A ``Select`` widget that uses a list of Spanish regions as its choices.
Switzerland (``ch``)
====================
.. class:: ch.forms.CHIdentityCardNumberField
A form field that validates input as a Swiss identity card number.
A valid number must confirm to the X1234567<0 or 1234567890 format and
have the correct checksums -- see http://adi.kousz.ch/artikel/IDCHE.htm.
.. class:: ch.forms.CHPhoneNumberField
A form field that validates input as a Swiss phone number. The correct
format is 0XX XXX XX XX. 0XX.XXX.XX.XX and 0XXXXXXXXX validate but are
corrected to 0XX XXX XX XX.
.. class:: ch.forms.CHZipCodeField
A form field that validates input as a Swiss zip code. Valid codes
consist of four digits.
.. class:: ch.forms.CHStateSelect
A ``Select`` widget that uses a list of Swiss states as its choices.
United Kingdom (``uk``)
=======================
.. class:: uk.forms.UKPostcodeField
A form field that validates input as a UK postcode. The regular
expression used is sourced from the schema for British Standard BS7666
address types at http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd.
.. class:: ch.forms.UKCountySelect
A ``Select`` widget that uses a list of UK counties/regions as its choices.
.. class:: ch.forms.UKNationSelect
A ``Select`` widget that uses a list of UK nations as its choices.
United States of America (``us``)
=================================
.. class:: us.forms.USPhoneNumberField
A form field that validates input as a U.S. phone number.
.. class:: us.forms.USSocialSecurityNumberField
A form field that validates input as a U.S. Social Security Number (SSN).
A valid SSN must obey the following rules:
* Format of XXX-XX-XXXX
* No group of digits consisting entirely of zeroes
* Leading group of digits cannot be 666
* Number not in promotional block 987-65-4320 through 987-65-4329
* Number not one known to be invalid due to widespread promotional
use or distribution (e.g., the Woolworth's number or the 1962
promotional number)
.. class:: us.forms.USStateField
A form field that validates input as a U.S. state name or abbreviation. It
normalizes the input to the standard two-letter postal service abbreviation
for the given state.
.. class:: us.forms.USZipCodeField
A form field that validates input as a U.S. ZIP code. Valid formats are
XXXXX or XXXXX-XXXX.
.. class:: us.forms.USStateSelect
A form ``Select`` widget that uses a list of U.S. states/territories as its
choices.

View file

@ -0,0 +1,72 @@
.. _ref-contrib-redirects:
=================
The redirects app
=================
.. module:: django.contrib.redirects
:synopsis: A framework for managing redirects.
Django comes with an optional redirects application. It lets you store simple
redirects in a database and handles the redirecting for you.
Installation
============
To install the redirects app, follow these steps:
1. Add ``'django.contrib.redirects'`` to your :setting:`INSTALLED_APPS`
setting.
2. Add ``'django.contrib.redirects.middleware.RedirectFallbackMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting.
3. Run the command :djadmin:`manage.py syncdb <syncdb>`.
How it works
============
``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.
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
:setting:`SITE_ID` setting.
* If it finds a match, and ``new_path`` is not empty, it redirects to
``new_path``.
* If it finds a match, and ``new_path`` is empty, it sends a 410 ("Gone")
HTTP header and empty (content-less) response.
* If it doesn't find a match, the request continues to be processed as
usual.
The middleware only gets activated for 404s -- not for 500s or responses of any
other status code.
Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you
can put ``RedirectFallbackMiddleware`` at the end of the list, because it's a
last resort.
For more on middleware, read the :ref:`middleware docs
<topics-http-middleware>`.
How to add, change and delete redirects
=======================================
Via the admin interface
-----------------------
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.
Via the Python API
------------------
.. class:: models.Redirect
Redirects are represented by a standard :ref:`Django model <topics-db-models>`,
which lives in `django/contrib/redirects/models.py`_. You can access redirect
objects via the :ref:`Django database API <topics-db-queries>`.
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py

View file

@ -0,0 +1,343 @@
.. _ref-contrib-sitemaps:
=====================
The sitemap framework
=====================
.. module:: django.contrib.sitemaps
:synopsis: A framework for generating Google sitemap XML files.
Django comes with a high-level sitemap-generating framework that makes
creating sitemap_ XML files easy.
.. _sitemap: http://www.sitemaps.org/
Overview
========
A sitemap is an XML file on your Web site that tells search-engine indexers how
frequently your pages change and how "important" certain pages are in relation
to other pages on your site. This information helps search engines index your
site.
The Django sitemap framework automates the creation of this XML file by letting
you express this information in Python code.
It works much like Django's :ref:`syndication framework
<ref-contrib-syndication>`. To create a sitemap, just write a
:class:`~django.contrib.sitemaps.Sitemap` class and point to it in your
:ref:`URLconf <topics-http-urls>`.
Installation
============
To install the sitemap app, follow these steps:
1. Add ``'django.contrib.sitemaps'`` to your :setting:`INSTALLED_APPS`
setting.
2. Make sure ``'django.template.loaders.app_directories.load_template_source'``
is in your :setting:`TEMPLATE_LOADERS` setting. It's in there by default,
so you'll only need to change this if you've changed that setting.
3. Make sure you've installed the
:mod:`sites framework <django.contrib.sites>`.
(Note: The sitemap application doesn't install any database tables. The only
reason it needs to go into :setting:`INSTALLED_APPS` is so that the
:func:`~django.template.loaders.app_directories.load_template_source` template
loader can find the default templates.)
Initialization
==============
To activate sitemap generation on your Django site, add this line to your
:ref:`URLconf <topics-http-urls>`::
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
This tells Django to build a sitemap when a client accesses :file:`/sitemap.xml`.
The name of the sitemap file is not important, but the location is. Search
engines will only index links in your sitemap for the current URL level and
below. For instance, if :file:`sitemap.xml` lives in your root directory, it may
reference any URL in your site. However, if your sitemap lives at
:file:`/content/sitemap.xml`, it may only reference URLs that begin with
:file:`/content/`.
The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
``sitemaps`` should be a dictionary that maps a short section label (e.g.,
``blog`` or ``news``) to its :class:`~django.contrib.sitemaps.Sitemap` class
(e.g., ``BlogSitemap`` or ``NewsSitemap``). It may also map to an *instance* of
a :class:`~django.contrib.sitemaps.Sitemap` class (e.g.,
``BlogSitemap(some_var)``).
Sitemap classes
===============
A :class:`~django.contrib.sitemaps.Sitemap` class is a simple Python
class that represents a "section" of entries in your sitemap. For example,
one :class:`~django.contrib.sitemaps.Sitemap` class could represent
all the entries of your weblog, while another could represent all of the
events in your events calendar.
In the simplest case, all these sections get lumped together into one
:file:`sitemap.xml`, but it's also possible to use the framework to generate a
sitemap index that references individual sitemap files, one per section. (See
`Creating a sitemap index`_ below.)
:class:`~django.contrib.sitemaps.Sitemap` classes must subclass
``django.contrib.sitemaps.Sitemap``. They can live anywhere in your codebase.
A simple example
================
Let's assume you have a blog system, with an ``Entry`` model, and you want your
sitemap to include all the links to your individual blog entries. Here's how
your sitemap class might look::
from django.contrib.sitemaps import Sitemap
from mysite.blog.models import Entry
class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Entry.objects.filter(is_draft=False)
def lastmod(self, obj):
return obj.pub_date
Note:
* :attr:`~Sitemap.changefreq` and :attr:`~Sitemap.priority` are class
attributes corresponding to ``<changefreq>`` and ``<priority>`` elements,
respectively. They can be made callable as functions, as
:attr:`~Sitemap.lastmod` was in the example.
* :attr:`~Sitemap.items()` is simply a method that returns a list of
objects. The objects returned will get passed to any callable methods
corresponding to a sitemap property (:attr:`~Sitemap.location`,
:attr:`~Sitemap.lastmod`, :attr:`~Sitemap.changefreq`, and
:attr:`~Sitemap.priority`).
* :attr:`~Sitemap.lastmod` should return a Python ``datetime`` object.
* There is no :attr:`~Sitemap.location` method in this example, but you
can provide it in order to specify the URL for your object. By default,
:attr:`~Sitemap.location()` calls ``get_absolute_url()`` on each object
and returns the result.
Sitemap class reference
=======================
.. class:: Sitemap
A ``Sitemap`` class can define the following methods/attributes:
.. attribute:: Sitemap.items
**Required.** A method that returns a list of objects. The framework
doesn't care what *type* of objects they are; all that matters is that
these objects get passed to the :attr:`~Sitemap.location()`,
:attr:`~Sitemap.lastmod()`, :attr:`~Sitemap.changefreq()` and
:attr:`~Sitemap.priority()` methods.
.. attribute:: Sitemap.location
**Optional.** Either a method or attribute.
If it's a method, it should return the absolute URL for a given object as
returned by :attr:`~Sitemap.items()`.
If it's an attribute, its value should be a string representing an absolute URL
to use for *every* object returned by :attr:`~Sitemap.items()`.
In both cases, "absolute URL" means a URL that doesn't include the protocol or
domain. Examples:
* Good: :file:`'/foo/bar/'`
* Bad: :file:`'example.com/foo/bar/'`
* Bad: :file:`'http://example.com/foo/bar/'`
If :attr:`~Sitemap.location` isn't provided, the framework will call the
``get_absolute_url()`` method on each object as returned by
:attr:`~Sitemap.items()`.
.. attribute:: Sitemap.lastmod
**Optional.** Either a method or attribute.
If it's a method, it should take one argument -- an object as returned by
:attr:`~Sitemap.items()` -- and return that object's last-modified date/time, as a Python
``datetime.datetime`` object.
If it's an attribute, its value should be a Python ``datetime.datetime`` object
representing the last-modified date/time for *every* object returned by
:attr:`~Sitemap.items()`.
.. attribute:: Sitemap.changefreq
**Optional.** Either a method or attribute.
If it's a method, it should take one argument -- an object as returned by
:attr:`~Sitemap.items()` -- and return that object's change frequency, as a Python string.
If it's an attribute, its value should be a string representing the change
frequency of *every* object returned by :attr:`~Sitemap.items()`.
Possible values for :attr:`~Sitemap.changefreq`, whether you use a method or attribute, are:
* ``'always'``
* ``'hourly'``
* ``'daily'``
* ``'weekly'``
* ``'monthly'``
* ``'yearly'``
* ``'never'``
.. method:: Sitemap.priority
**Optional.** Either a method or attribute.
If it's a method, it should take one argument -- an object as returned by
:attr:`~Sitemap.items()` -- and return that object's priority, as either a string or float.
If it's an attribute, its value should be either a string or float representing
the priority of *every* object returned by :attr:`~Sitemap.items()`.
Example values for :attr:`~Sitemap.priority`: ``0.4``, ``1.0``. The default priority of a
page is ``0.5``. See the `sitemaps.org documentation`_ for more.
.. _sitemaps.org documentation: http://www.sitemaps.org/protocol.html#prioritydef
Shortcuts
=========
The sitemap framework provides a couple convenience classes for common cases:
.. class:: FlatPageSitemap
The :class:`django.contrib.sitemaps.FlatPageSitemap` class looks at all
:mod:`flatpages <django.contrib.flatpages>` defined for the current
:setting:`SITE_ID` (see the
:mod:`sites documentation <django.contrib.sites>`) and
creates an entry in the sitemap. These entries include only the
:attr:`~Sitemap.location` attribute -- not :attr:`~Sitemap.lastmod`,
:attr:`~Sitemap.changefreq` or :attr:`~Sitemap.priority`.
.. class:: GenericSitemap
The :class:`django.contrib.sitemaps.GenericSitemap` class works with any
:ref:`generic views <ref-generic-views>` you already have.
To use it, create an instance, passing in the same :data:`info_dict` you pass to
the generic views. The only requirement is that the dictionary have a
:data:`queryset` entry. It may also have a :data:`date_field` entry that specifies a
date field for objects retrieved from the :data:`queryset`. This will be used for
the :attr:`~Sitemap.lastmod` attribute in the generated sitemap. You may
also pass :attr:`~Sitemap.priority` and :attr:`~Sitemap.changefreq`
keyword arguments to the :class:`~django.contrib.sitemaps.GenericSitemap`
constructor to specify these attributes for all URLs.
Example
-------
Here's an example of a :ref:`URLconf <topics-http-urls>` using both::
from django.conf.urls.defaults import *
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from mysite.blog.models import Entry
info_dict = {
'queryset': Entry.objects.all(),
'date_field': 'pub_date',
}
sitemaps = {
'flatpages': FlatPageSitemap,
'blog': GenericSitemap(info_dict, priority=0.6),
}
urlpatterns = patterns('',
# some generic view using info_dict
# ...
# the sitemap
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
)
.. _URLconf: ../url_dispatch/
Creating a sitemap index
========================
The sitemap framework also has the ability to create a sitemap index that
references individual sitemap files, one per each section defined in your
:data:`sitemaps` dictionary. The only differences in usage are:
* You use two views in your URLconf: :func:`django.contrib.sitemaps.views.index`
and :func:`django.contrib.sitemaps.views.sitemap`.
* The :func:`django.contrib.sitemaps.views.sitemap` view should take a
:data:`section` keyword argument.
Here is what the relevant URLconf lines would look like for the example above::
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps})
(r'^sitemap-(?P<section>.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
This will automatically generate a :file:`sitemap.xml` file that references both
:file:`sitemap-flatpages.xml` and :file:`sitemap-blog.xml`. The
:class:`~django.contrib.sitemaps.Sitemap` classes and the :data:`sitemaps` dict
don't change at all.
If one of your sitemaps is going to have more than 50,000 URLs you should
create an index file. Your sitemap will be paginated and the index will
reflect that.
Pinging Google
==============
You may want to "ping" Google when your sitemap changes, to let it know to
reindex your site. The framework provides a function to do just that:
:func:`django.contrib.sitemaps.ping_google()`.
.. function:: ping_google
:func:`ping_google` takes an optional argument, :data:`sitemap_url`,
which should be the absolute URL of your site's sitemap (e.g.,
:file:`'/sitemap.xml'`). If this argument isn't provided,
:func:`ping_google` will attempt to figure out your
sitemap by performing a reverse looking in your URLconf.
:func:`ping_google` raises the exception
:exc:`django.contrib.sitemaps.SitemapNotFound` if it cannot determine your
sitemap URL.
One useful way to call :func:`ping_google` is from a model's ``save()``
method::
from django.contrib.sitemaps import ping_google
class Entry(models.Model):
# ...
def save(self):
super(Entry, self).save()
try:
ping_google()
except Exception:
# Bare 'except' because we could get a variety
# of HTTP-related exceptions.
pass
A more efficient solution, however, would be to call :func:`ping_google` from a
cron script, or some other scheduled task. The function makes an HTTP request
to Google's servers, so you may not want to introduce that network overhead
each time you call ``save()``.
Pinging Google via `manage.py`
------------------------------
**New in Django development version**
Once the sitemaps application is added to your project, you may also
ping the Google server's through the command line manage.py interface::
python manage.py ping_google [/sitemap.xml]

401
docs/ref/contrib/sites.txt Normal file
View file

@ -0,0 +1,401 @@
.. _ref-contrib-sites:
=====================
The "sites" framework
=====================
.. module:: django.contrib.sites
:synopsis: Lets you operate multiple web sites from the same database and
Django project
Django comes with an optional "sites" framework. It's a hook for associating
objects and functionality to particular Web sites, and it's a holding place for
the domain names and "verbose" names of your Django-powered sites.
Use it if your single Django installation powers more than one site and you
need to differentiate between those sites in some way.
The whole sites framework is based on a simple model:
.. class:: django.contrib.sites.models.Site
This model has :attr:`~django.contrib.sites.models.Site.domain` and
:attr:`~django.contrib.sites.models.Site.name` fields. The :setting:`SITE_ID`
setting specifies the database ID of the
:class:`~django.contrib.sites.models.Site` object associated with that
particular settings file.
How you use this is up to you, but Django uses it in a couple of ways
automatically via simple conventions.
Example usage
=============
Why would you use sites? It's best explained through examples.
Associating content with multiple sites
---------------------------------------
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 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. In Django model terminology,
that's represented by a :class:`~django.db.models.ManyToManyField` in the
``Article`` model::
from django.db import models
from django.contrib.sites.models import Site
class Article(models.Model):
headline = models.CharField(max_length=200)
# ...
sites = models.ManyToManyField(Site)
This accomplishes several things quite nicely:
* It lets the site producers edit all content -- on both sites -- in a
single interface (the Django admin).
* It means the same story doesn't have to be published twice in the
database; it only has a single record in the database.
* It lets the site developers use the same Django view code for both sites.
The view code that displays a given story just checks to make sure the
requested story is on the current site. It looks something like this::
from django.conf import settings
def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id, sites__id__exact=settings.SITE_ID)
except Article.DoesNotExist:
raise Http404
# ...
.. _ljworld.com: http://www.ljworld.com/
.. _lawrence.com: http://www.lawrence.com/
Associating content with a single site
--------------------------------------
Similarly, you can associate a model to the :class:`~django.contrib.sites.models.Site`
model in a many-to-one relationship, using
:class:`~django.db.models.fields.related.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(max_length=200)
# ...
site = models.ForeignKey(Site)
This has the same benefits as described in the last section.
Hooking into the current site from views
----------------------------------------
On a lower level, you can use the sites framework in your Django views to do
particular things based on the 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 :class:`~django.contrib.sites.models.Site` object
for the value of :setting:`settings.SITE_ID <SITE_ID>` is quite common, so
the :class:`~django.contrib.sites.models.Site` model's manager 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.
Getting the current domain for display
--------------------------------------
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
:class:`~django.contrib.sites.models.Site`
objects, we can abstract the "thank you" notice to use the values of the
current site's :attr:`~django.contrib.sites.models.Site.name` and
:attr:`~django.contrib.sites.models.Site.domain`.
Here's an example of what the form-handling view looks like::
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])
# ...
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." Same goes for 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 (:setting:`TEMPLATE_DIRS`), you could simply farm out
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, 'editor@ljworld.com', [user.email])
# ...
In this case, you'd have to create :file:`subject.txt` and :file:`message.txt` template
files for 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 :class:`~django.contrib.sites.models.Site``
objects as much as possible, to remove unneeded complexity and redundancy.
Getting the current domain for full URLs
----------------------------------------
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/'
Caching the current ``Site`` object
===================================
**New in Django development version**
As the current site is stored in the database, each call to
``Site.objects.get_current()`` could result in a database query. But Django is a
little cleverer than that: on the first request, the current site is cached, and
any subsequent call returns the cached data instead of hitting the database.
If for any reason you want to force a database query, you can tell Django to
clear the cache using ``Site.objects.clear_cache()``::
# First call; current site fetched from database.
current_site = Site.objects.get_current()
# ...
# Second call; current site fetched from cache.
current_site = Site.objects.get_current()
# ...
# Force a database query for the third call.
Site.objects.clear_cache()
current_site = Site.objects.get_current()
The ``CurrentSiteManager``
==========================
.. class:: django.contrib.sites.managers.CurrentSiteManager
If :class:`~django.contrib.sites.models.Site`\s play a key role in your application,
consider using the helpful
:class:`~django.contrib.sites.managers.CurrentSiteManager` in your model(s).
It's a model :ref:`manager <topics-db-managers>` that automatically filters
its queries to include only objects associated with the current
:class:`~django.contrib.sites.models.Site`.
Use :class:`~django.contrib.sites.managers.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(max_length=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 :setting:`SITE_ID` setting.
Put another way, these two statements are equivalent::
Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()
How did :class:`~django.contrib.sites.managers.CurrentSiteManager` know which
field of ``Photo`` was the :class:`~django.contrib.sites.models.Site`? It
defaults to looking for a field called
:class:`~django.contrib.sites.models.Site`. If your model has a
:class:`~django.db.models.fields.related.ForeignKey` or
:class:`~django.db.models.fields.related.ManyToManyField` called something
*other* than :class:`~django.contrib.sites.models.Site`, you need to explicitly
pass that as the parameter to
:class:`~django.contrib.sites.managers.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(max_length=100)
pub_date = models.DateField()
publish_on = models.ForeignKey(Site)
objects = models.Manager()
on_site = CurrentSiteManager('publish_on')
If you attempt to use :class:`~django.contrib.sites.managers.CurrentSiteManager`
and pass a field name that doesn't exist, Django will raise a :exc:`ValueError`.
Finally, note that you'll probably want to keep a normal (non-site-specific)
``Manager`` on your model, even if you use
:class:`~django.contrib.sites.managers.CurrentSiteManager`. As explained
in the :ref:`manager documentation <topics-db-managers>`, 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
:class:`~django.contrib.sites.managers.CurrentSiteManager`.
How Django uses the sites framework
===================================
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 :setting:`SITE_ID` setting.
Here's how Django uses the sites framework:
* In the :mod:`redirects framework <django.contrib.redirects>`, each
redirect object is associated with a particular site. When Django searches
for a redirect, it takes into account the current :setting:`SITE_ID`.
* In the comments framework, each comment is associated with a particular
site. When a comment is posted, its
:class:`~django.contrib.sites.models.Site` is set to the current
:setting:`SITE_ID`, and when comments are listed via the appropriate
template tag, only the comments for the current site are displayed.
* In the :mod:`flatpages framework <django.contrib.flatpages>`, each
flatpage is associated with a particular site. When a flatpage is created,
you specify its :class:`~django.contrib.sites.models.Site`, and the
:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
checks the current :setting:`SITE_ID` in retrieving flatpages to display.
* In the :mod:`syndication framework <django.contrib.syndication>`, the
templates for ``title`` and ``description`` automatically have access to a
variable ``{{ site }}``, which is the
:class:`~django.contrib.sites.models.Site` object representing the current
site. Also, the hook for providing item URLs will use the ``domain`` from
the current :class:`~django.contrib.sites.models.Site` object if you don't
specify a fully-qualified domain.
* In the :mod:`authentication framework <django.contrib.auth>`, the
:func:`django.contrib.auth.views.login` view passes the current
:class:`~django.contrib.sites.models.Site` name to the template as
``{{ site_name }}``.
* The shortcut view (:func:`django.views.defaults.shortcut`) uses the domain
of the current :class:`~django.contrib.sites.models.Site` object when
calculating an object's URL.
* In the admin framework, the "view on site" link uses the current
:class:`~django.contrib.sites.models.Site` to work out the domain for the
site that it will redirect to.
``RequestSite`` objects
=======================
.. _requestsite-objects:
**New in Django development version**
Some :ref:`django.contrib <ref-contrib-index>` applications take advantage of
the sites framework but are architected in a way that doesn't *require* the
sites framework to be installed in your database. (Some people don't want to, or
just aren't *able* to install the extra database table that the sites framework
requires.) For those cases, the framework provides a
:class:`~django.contrib.sites.models.RequestSite` class, which can be used as a
fallback when the database-backed sites framework is not available.
A :class:`~django.contrib.sites.models.RequestSite` object has a similar
interface to a normal :class:`~django.contrib.sites.models.Site` object, except
its :meth:`~django.contrib.sites.models.RequestSite.__init__()` method takes an
:class:`~django.http.HttpRequest` object. It's able to deduce the
:attr:`~django.contrib.sites.models.RequestSite.domain` and
:attr:`~django.contrib.sites.models.RequestSite.name` by looking at the
request's domain. It has :meth:`~django.contrib.sites.models.RequestSite.save()`
and :meth:`~django.contrib.sites.models.RequestSite.delete()` methods to match
the interface of :class:`~django.contrib.sites.models.Site`, but the methods
raise :exc:`NotImplementedError`.

View file

@ -0,0 +1,992 @@
.. _ref-contrib-syndication:
==============================
The syndication feed framework
==============================
.. module:: django.contrib.syndication
:synopsis: A framework for generating syndication feeds, in RSS and Atom,
quite easily.
Django comes with a high-level syndication-feed-generating framework that makes
creating RSS_ and Atom_ feeds easy.
To create any syndication feed, all you have to do is write a short Python
class. You can create as many feeds as you want.
Django also comes with a lower-level feed-generating API. Use this if you want
to generate feeds outside of a Web context, or in some other lower-level way.
.. _RSS: http://www.whatisrss.com/
.. _Atom: http://www.atomenabled.org/
The high-level framework
========================
Overview
--------
The high-level feed-generating framework is a view that's hooked to ``/feeds/``
by default. Django uses the remainder of the URL (everything after ``/feeds/``)
to determine which feed to output.
To create a feed, just write a :class:`~django.contrib.syndication.feeds.Feed`
class and point to it in your :ref:`URLconf <topics-http-urls>`.
Initialization
--------------
If you're not using the latest Django development version, you'll need to make
sure Django's sites framework is installed -- including its database table. (See
the :mod:`sites framework documentation <django.contrib.sites>` for more
information.) This has changed in the Django development version; the
syndication feed framework no longer requires the sites framework.
To activate syndication feeds on your Django site, add this line to your
:ref:`URLconf <topics-http-urls>`::
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
This tells Django to use the RSS framework to handle all URLs starting with
:file:`"feeds/"`. (You can change that :file:`"feeds/"` prefix to fit your own
needs.)
This URLconf line has an extra argument: ``{'feed_dict': feeds}``. Use this
extra argument to pass the syndication framework the feeds that should be
published under that URL.
Specifically, :data:`feed_dict` should be a dictionary that maps a feed's slug
(short URL label) to its :class:`~django.contrib.syndication.feeds.Feed` class.
You can define the ``feed_dict`` in the URLconf itself. Here's a full example
URLconf::
from django.conf.urls.defaults import *
from myproject.feeds import LatestEntries, LatestEntriesByCategory
feeds = {
'latest': LatestEntries,
'categories': LatestEntriesByCategory,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
The above example registers two feeds:
* The feed represented by ``LatestEntries`` will live at ``feeds/latest/``.
* The feed represented by ``LatestEntriesByCategory`` will live at
``feeds/categories/``.
Once that's set up, you just need to define the
:class:`~django.contrib.syndication.feeds.Feed` classes themselves.
Feed classes
------------
A :class:`~django.contrib.syndication.feeds.Feed` class is a simple Python class
that represents a syndication feed. A feed can be simple (e.g., a "site news"
feed, or a basic feed displaying the latest entries of a blog) or more complex
(e.g., a feed displaying all the blog entries in a particular category, where
the category is variable).
:class:`~django.contrib.syndication.feeds.Feed` classes must subclass
``django.contrib.syndication.feeds.Feed``. They can live anywhere in your
codebase.
A simple example
----------------
This simple example, taken from `chicagocrime.org`_, describes a feed of the
latest five news items::
from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem
class LatestEntries(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
Note:
* The class subclasses ``django.contrib.syndication.feeds.Feed``.
* :attr:`title`, :attr:`link` and :attr:`description` correspond to the
standard RSS ``<title>``, ``<link>`` and ``<description>`` elements,
respectively.
* :meth:`items()` is, simply, a method that returns a list of objects that
should be included in the feed as ``<item>`` elements. Although this
example returns ``NewsItem`` objects using Django's
:ref:`object-relational mapper <ref-models-querysets>`, :meth:`items()`
doesn't have to return model instances. Although you get a few bits of
functionality "for free" by using Django models, :meth:`items()` can
return any type of object you want.
* If you're creating an Atom feed, rather than an RSS feed, set the
:attr:`subtitle` attribute instead of the :attr:`description` attribute.
See `Publishing Atom and RSS feeds in tandem`_, later, for an example.
One thing's left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
``<link>`` and ``<description>``. We need to tell the framework what data to put
into those elements.
* To specify the contents of ``<title>`` and ``<description>``, create
:ref:`Django templates <topics-templates>` called
:file:`feeds/latest_title.html`` and
:file:`feeds/latest_description.html`, where :attr:`latest` is the
:attr:`slug` specified in the URLconf for the given feed. Note the
``.html`` extension is required. The RSS system renders that template for
each item, passing it two template context variables:
* ``{{ obj }}`` -- The current object (one of whichever objects you
returned in :meth:`items()`).
* ``{{ site }}`` -- A :class:`django.contrib.sites.models.Site` object
representing the current site. This is useful for ``{{ site.domain
}}`` or ``{{ site.name }}``. Note that if you're using the latest
Django development version and do *not* have the Django sites
framework installed, this will be set to a
:class:`django.contrib.sites.models.RequestSite` object. See the
:ref:`RequestSite section of the sites framework documentation
<requestsite-objects>` for more.
If you don't create a template for either the title or description, the
framework will use the template ``"{{ obj }}"`` by default -- that is, the
normal string representation of the object. You can also change the names
of these two templates by specifying ``title_template`` and
``description_template`` as attributes of your
:class:`~django.contrib.syndication.feeds.Feed` class.
* To specify the contents of ``<link>``, you have two options. For each item
in :meth:`items()`, Django first tries executing a ``get_absolute_url()``
method on that object. If that method doesn't exist, it tries calling a
method :meth:`item_link()` in the
:class:`~django.contrib.syndication.feeds.Feed` class, passing it a single
parameter, :attr:`item`, which is the object itself. Both
``get_absolute_url()`` and :meth:`item_link()` should return the item's
URL as a normal Python string. As with ``get_absolute_url()``, the result
of :meth:`item_link()` will be included directly in the URL, so you are
responsible for doing all necessary URL quoting and conversion to ASCII
inside the method itself.
* For the LatestEntries example above, we could have very simple feed
templates:
* latest_title.html:
.. code-block:: html+django
{{ obj.title }}
* latest_description.html:
.. code-block:: html+django
{{ obj.description }}
.. _chicagocrime.org: http://www.chicagocrime.org/
A complex example
-----------------
The framework also supports more complex feeds, via parameters.
For example, `chicagocrime.org`_ offers an RSS feed of recent crimes for every
police beat in Chicago. It'd be silly to create a separate
:class:`~django.contrib.syndication.feeds.Feed` class for each police beat; that
would violate the :ref:`DRY principle <dry>` and would couple data to
programming logic. Instead, the syndication framework lets you make generic
feeds that output items based on information in the feed's URL.
On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
* :file:`/rss/beats/0613/` -- Returns recent crimes for beat 0613.
* :file:`/rss/beats/1424/` -- Returns recent crimes for beat 1424.
The slug here is ``"beats"``. The syndication framework sees the extra URL bits
after the slug -- ``0613`` and ``1424`` -- and gives you a hook to tell it what
those URL bits mean, and how they should influence which items get published in
the feed.
An example makes this clear. Here's the code for these beat-specific feeds::
from django.contrib.syndication.feeds import FeedDoesNotExist
class BeatFeed(Feed):
def get_object(self, bits):
# In case of "/rss/beats/0613/foo/bar/baz/", or other such clutter,
# check that bits has only one member.
if len(bits) != 1:
raise ObjectDoesNotExist
return Beat.objects.get(beat__exact=bits[0])
def title(self, obj):
return "Chicagocrime.org: Crimes for beat %s" % obj.beat
def link(self, obj):
if not obj:
raise FeedDoesNotExist
return obj.get_absolute_url()
def description(self, obj):
return "Crimes recently reported in police beat %s" % obj.beat
def items(self, obj):
return Crime.objects.filter(beat__id__exact=obj.id).order_by('-crime_date')[:30]
Here's the basic algorithm the RSS framework follows, given this class and a
request to the URL :file:`/rss/beats/0613/`:
* The framework gets the URL :file:`/rss/beats/0613/` and notices there's an
extra bit of URL after the slug. It splits that remaining string by the
slash character (``"/"``) and calls the
:class:`~django.contrib.syndication.feeds.Feed` class'
:meth:`get_object()` method, passing it the bits. In this case, bits is
``['0613']``. For a request to :file:`/rss/beats/0613/foo/bar/`, bits
would be ``['0613', 'foo', 'bar']``.
* :meth:`get_object()` is responsible for retrieving the given beat, from
the given ``bits``. In this case, it uses the Django database API to
retrieve the beat. Note that :meth:`get_object()` should raise
:exc:`django.core.exceptions.ObjectDoesNotExist` if given invalid
parameters. There's no ``try``/``except`` around the
``Beat.objects.get()`` call, because it's not necessary; that function
raises :exc:`Beat.DoesNotExist` on failure, and :exc:`Beat.DoesNotExist`
is a subclass of :exc:`ObjectDoesNotExist`. Raising
:exc:`ObjectDoesNotExist` in :meth:`get_object()` tells Django to produce
a 404 error for that request.
**New in Django development version:** The :meth:`get_object()` method
also has a chance to handle the :file:`/rss/beats/` url. In this case,
:data:`bits` will be an empty list. In our example, ``len(bits) != 1`` and
an :exc:`ObjectDoesNotExist` exception will be raised, so
:file:`/rss/beats/` will generate a 404 page. But you can handle this case
however you like. For example, you could generate a combined feed for all
beats.
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
Django uses the :meth:`title()`, :meth:`link()` and :meth:`description()`
methods. In the previous example, they were simple string class
attributes, but this example illustrates that they can be either strings
*or* methods. For each of :attr:`title`, :attr:`link` and
:attr:`description`, Django follows this algorithm:
* First, it tries to call a method, passing the ``obj`` argument, where
``obj`` is the object returned by :meth:`get_object()`.
* Failing that, it tries to call a method with no arguments.
* Failing that, it uses the class attribute.
Inside the :meth:`link()` method, we handle the possibility that ``obj``
might be ``None``, which can occur when the URL isn't fully specified. In
some cases, you might want to do something else in this case, which would
mean you'd need to check for ``obj`` existing in other methods as well.
(The :meth:`link()` method is called very early in the feed generation
process, so it's a good place to bail out early.)
* Finally, note that :meth:`items()` in this example also takes the ``obj``
argument. The algorithm for :attr:`items` is the same as described in the
previous step -- first, it tries :meth:`items(obj)`, then :meth:`items()`,
then finally an :attr:`items` class attribute (which should be a list).
The ``ExampleFeed`` class below gives full documentation on methods and
attributes of :class:`~django.contrib.syndication.feeds.Feed` classes.
Specifying the type of feed
---------------------------
By default, feeds produced in this framework use RSS 2.0.
To change that, add a ``feed_type`` attribute to your
:class:`~django.contrib.syndication.feeds.Feed` class, like so::
from django.utils.feedgenerator import Atom1Feed
class MyFeed(Feed):
feed_type = Atom1Feed
Note that you set ``feed_type`` to a class object, not an instance.
Currently available feed types are:
* :class:`django.utils.feedgenerator.Rss201rev2Feed` (RSS 2.01. Default.)
* :class:`django.utils.feedgenerator.RssUserland091Feed` (RSS 0.91.)
* :class:`django.utils.feedgenerator.Atom1Feed` (Atom 1.0.)
Enclosures
----------
To specify enclosures, such as those used in creating podcast feeds, use the
:attr:`item_enclosure_url`, :attr:`item_enclosure_length` and
:attr:`item_enclosure_mime_type` hooks. See the ``ExampleFeed`` class below for
usage examples.
Language
--------
Feeds created by the syndication framework automatically include the
appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
comes directly from your :setting:`LANGUAGE_CODE setting`.
URLs
----
The :attr:`link` method/attribute can return either an absolute URL (e.g.
:file:`"/blog/"`) or a URL with the fully-qualified domain and protocol (e.g.
``"http://www.example.com/blog/"``). If :attr:`link` doesn't return the domain,
the syndication framework will insert the domain of the current site, according
to your :setting:`SITE_ID setting <SITE_ID>`.
Atom feeds require a ``<link rel="self">`` that defines the feed's current
location. The syndication framework populates this automatically, using the
domain of the current site according to the :setting:`SITE_ID` setting.
Publishing Atom and RSS feeds in tandem
---------------------------------------
Some developers like to make available both Atom *and* RSS versions of their
feeds. That's easy to do with Django: Just create a subclass of your
:class:`~django.contrib.syndication.feeds.Feed`
class and set the :attr:`feed_type` to something different. Then update your
URLconf to add the extra versions.
Here's a full example::
from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem
from django.utils.feedgenerator import Atom1Feed
class RssSiteNewsFeed(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
class AtomSiteNewsFeed(RssSiteNewsFeed):
feed_type = Atom1Feed
subtitle = RssSiteNewsFeed.description
.. Note::
In this example, the RSS feed uses a :attr:`description` while the Atom
feed uses a :attr:`subtitle`. That's because Atom feeds don't provide for
a feed-level "description," but they *do* provide for a "subtitle."
If you provide a :attr:`description` in your
:class:`~django.contrib.syndication.feeds.Feed` class, Django will *not*
automatically put that into the :attr:`subtitle` element, because a
subtitle and description are not necessarily the same thing. Instead, you
should define a :attr:`subtitle` attribute.
In the above example, we simply set the Atom feed's :attr:`subtitle` to the
RSS feed's :attr:`description`, because it's quite short already.
And the accompanying URLconf::
from django.conf.urls.defaults import *
from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
feeds = {
'rss': RssSiteNewsFeed,
'atom': AtomSiteNewsFeed,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
Feed class reference
--------------------
.. class:: django.contrib.syndication.feeds.Feed
This example illustrates all possible attributes and methods for a
:class:`~django.contrib.syndication.feeds.Feed` class::
from django.contrib.syndication.feeds import Feed
from django.utils import feedgenerator
class ExampleFeed(Feed):
# FEED TYPE -- Optional. This should be a class that subclasses
# django.utils.feedgenerator.SyndicationFeed. This designates which
# type of feed this should be: RSS 2.0, Atom 1.0, etc.
# If you don't specify feed_type, your feed will be RSS 2.0.
# This should be a class, not an instance of the class.
feed_type = feedgenerator.Rss201rev2Feed
# TEMPLATE NAMES -- Optional. These should be strings representing
# names of Django templates that the system should use in rendering the
# title and description of your feed items. Both are optional.
# If you don't specify one, or either, Django will use the template
# 'feeds/SLUG_title.html' and 'feeds/SLUG_description.html', where SLUG
# is the slug you specify in the URL.
title_template = None
description_template = None
# TITLE -- One of the following three is required. The framework looks
# for them in this order.
def title(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
title as a normal Python string.
"""
def title(self):
"""
Returns the feed's title as a normal Python string.
"""
title = 'foo' # Hard-coded title.
# LINK -- One of the following three is required. The framework looks
# for them in this order.
def link(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
link as a normal Python string.
"""
def link(self):
"""
Returns the feed's link as a normal Python string.
"""
link = '/foo/bar/' # Hard-coded link.
# GUID -- One of the following three is optional. The framework looks
# for them in this order. This property is only used for Atom feeds
# (where it is the feed-level ID element). If not provided, the feed
# link is used as the ID.
#
# (New in Django development version)
def feed_guid(self, obj):
"""
Takes the object returned by get_object() and returns the globally
unique ID for the feed as a normal Python string.
"""
def feed_guid(self):
"""
Returns the feed's globally unique ID as a normal Python string.
"""
feed_guid = '/foo/bar/1234' # Hard-coded guid.
# DESCRIPTION -- One of the following three is required. The framework
# looks for them in this order.
def description(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
description as a normal Python string.
"""
def description(self):
"""
Returns the feed's description as a normal Python string.
"""
description = 'Foo bar baz.' # Hard-coded description.
# AUTHOR NAME --One of the following three is optional. The framework
# looks for them in this order.
def author_name(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's name as a normal Python string.
"""
def author_name(self):
"""
Returns the feed's author's name as a normal Python string.
"""
author_name = 'Sally Smith' # Hard-coded author name.
# AUTHOR E-MAIL --One of the following three is optional. The framework
# looks for them in this order.
def author_email(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's e-mail as a normal Python string.
"""
def author_email(self):
"""
Returns the feed's author's e-mail as a normal Python string.
"""
author_email = 'test@example.com' # Hard-coded author e-mail.
# AUTHOR LINK --One of the following three is optional. The framework
# looks for them in this order. In each case, the URL should include
# the "http://" and domain name.
def author_link(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's URL as a normal Python string.
"""
def author_link(self):
"""
Returns the feed's author's URL as a normal Python string.
"""
author_link = 'http://www.example.com/' # Hard-coded author URL.
# CATEGORIES -- One of the following three is optional. The framework
# looks for them in this order. In each case, the method/attribute
# should return an iterable object that returns strings.
def categories(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
categories as iterable over strings.
"""
def categories(self):
"""
Returns the feed's categories as iterable over strings.
"""
categories = ("python", "django") # Hard-coded list of categories.
# COPYRIGHT NOTICE -- One of the following three is optional. The
# framework looks for them in this order.
def copyright(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
copyright notice as a normal Python string.
"""
def copyright(self):
"""
Returns the feed's copyright notice as a normal Python string.
"""
copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
# TTL -- One of the following three is optional. The framework looks
# for them in this order. Ignored for Atom feeds.
def ttl(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
TTL (Time To Live) as a normal Python string.
"""
def ttl(self):
"""
Returns the feed's TTL as a normal Python string.
"""
ttl = 600 # Hard-coded Time To Live.
# ITEMS -- One of the following three is required. The framework looks
# for them in this order.
def items(self, obj):
"""
Takes the object returned by get_object() and returns a list of
items to publish in this feed.
"""
def items(self):
"""
Returns a list of items to publish in this feed.
"""
items = ('Item 1', 'Item 2') # Hard-coded items.
# GET_OBJECT -- This is required for feeds that publish different data
# for different URL parameters. (See "A complex example" above.)
def get_object(self, bits):
"""
Takes a list of strings gleaned from the URL and returns an object
represented by this feed. Raises
django.core.exceptions.ObjectDoesNotExist on error.
"""
# ITEM LINK -- One of these three is required. The framework looks for
# them in this order.
# First, the framework tries the two methods below, in
# order. Failing that, it falls back to the get_absolute_url()
# method on each item returned by items().
def item_link(self, item):
"""
Takes an item, as returned by items(), and returns the item's URL.
"""
def item_link(self):
"""
Returns the URL for every item in the feed.
"""
# ITEM_GUID -- The following method is optional. This property is
# only used for Atom feeds (it is the ID element for an item in an
# Atom feed). If not provided, the item's link is used by default.
#
# (New in Django development version)
def item_guid(self, obj):
"""
Takes an item, as return by items(), and returns the item's ID.
"""
# ITEM AUTHOR NAME -- One of the following three is optional. The
# framework looks for them in this order.
def item_author_name(self, item):
"""
Takes an item, as returned by items(), and returns the item's
author's name as a normal Python string.
"""
def item_author_name(self):
"""
Returns the author name for every item in the feed.
"""
item_author_name = 'Sally Smith' # Hard-coded author name.
# ITEM AUTHOR E-MAIL --One of the following three is optional. The
# framework looks for them in this order.
#
# If you specify this, you must specify item_author_name.
def item_author_email(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
author's e-mail as a normal Python string.
"""
def item_author_email(self):
"""
Returns the author e-mail for every item in the feed.
"""
item_author_email = 'test@example.com' # Hard-coded author e-mail.
# ITEM AUTHOR LINK --One of the following three is optional. The
# framework looks for them in this order. In each case, the URL should
# include the "http://" and domain name.
#
# If you specify this, you must specify item_author_name.
def item_author_link(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
author's URL as a normal Python string.
"""
def item_author_link(self):
"""
Returns the author URL for every item in the feed.
"""
item_author_link = 'http://www.example.com/' # Hard-coded author URL.
# ITEM ENCLOSURE URL -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
def item_enclosure_url(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure URL.
"""
def item_enclosure_url(self):
"""
Returns the enclosure URL for every item in the feed.
"""
item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
# ITEM ENCLOSURE LENGTH -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
# In each case, the returned value should be either an integer, or a
# string representation of the integer, in bytes.
def item_enclosure_length(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure length.
"""
def item_enclosure_length(self):
"""
Returns the enclosure length for every item in the feed.
"""
item_enclosure_length = 32000 # Hard-coded enclosure length.
# ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
def item_enclosure_mime_type(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure MIME type.
"""
def item_enclosure_mime_type(self):
"""
Returns the enclosure MIME type for every item in the feed.
"""
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
# ITEM PUBDATE -- It's optional to use one of these three. This is a
# hook that specifies how to get the pubdate for a given item.
# In each case, the method/attribute should return a Python
# datetime.datetime object.
def item_pubdate(self, item):
"""
Takes an item, as returned by items(), and returns the item's
pubdate.
"""
def item_pubdate(self):
"""
Returns the pubdate for every item in the feed.
"""
item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
# ITEM CATEGORIES -- It's optional to use one of these three. This is
# a hook that specifies how to get the list of categories for a given
# item. In each case, the method/attribute should return an iterable
# object that returns strings.
def item_categories(self, item):
"""
Takes an item, as returned by items(), and returns the item's
categories.
"""
def item_categories(self):
"""
Returns the categories for every item in the feed.
"""
item_categories = ("python", "django") # Hard-coded categories.
# ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
# following three is optional. The framework looks for them in this
# order.
def item_copyright(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
copyright notice as a normal Python string.
"""
def item_copyright(self):
"""
Returns the copyright notice for every item in the feed.
"""
item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
The low-level framework
=======================
Behind the scenes, the high-level RSS framework uses a lower-level framework
for generating feeds' XML. This framework lives in a single module:
`django/utils/feedgenerator.py`_.
You use this framework on your own, for lower-level feed generation. You can
also create custom feed generator subclasses for use with the ``feed_type``
``Feed`` option.
``SyndicationFeed`` classes
---------------------------
The :mod:`~django.utils.feedgenerator` module contains a base class:
.. class:: django.utils.feedgenerator.SyndicationFeed
and several subclasses:
.. class:: django.utils.feedgenerator.RssUserland091Feed
.. class:: django.utils.feedgenerator.Rss201rev2Feed
.. class:: django.utils.feedgenerator.Atom1Feed
Each of these three classes knows how to render a certain type of feed as XML.
They share this interface:
.. method:: SyndicationFeed.__init__(**kwargs)
Initialize the feed with the given dictionary of metadata, which applies to
the entire feed. Required keyword arguments are:
* ``title``
* ``link``
* ``description``
There's also a bunch of other optional keywords:
* ``language``
* ``author_email``
* ``author_name``
* ``author_link``
* ``subtitle``
* ``categories``
* ``feed_url``
* ``feed_copyright``
* ``feed_guid``
* ``ttl``
Any extra keyword arguments you pass to ``__init__`` will be stored in
``self.feed`` for use with `custom feed generators`_.
All parameters should be Unicode objects, except ``categories``, which
should be a sequence of Unicode objects.
.. method:: SyndicationFeed.add_item(**kwargs)
Add an item to the feed with the given parameters.
Required keyword arguments are:
* ``title``
* ``link``
* ``description``
Optional keyword arguments are:
* ``author_email``
* ``author_name``
* ``author_link``
* ``pubdate``
* ``comments``
* ``unique_id``
* ``enclosure``
* ``categories``
* ``item_copyright``
* ``ttl``
Extra keyword arguments will be stored for `custom feed generators`_.
All parameters, if given, should be Unicode objects, except:
* ``pubdate`` should be a `Python datetime object`_.
* ``enclosure`` should be an instance of ``feedgenerator.Enclosure``.
* ``categories`` should be a sequence of Unicode objects.
.. method:: SyndicationFeed.write(outfile, encoding)
Outputs the feed in the given encoding to outfile, which is a file-like object.
.. method:: SyndicationFeed.writeString(encoding)
Returns the feed as a string in the given encoding.
For example, to create an Atom 1.0 feed and print it to standard output::
>>> from django.utils import feedgenerator
>>> f = feedgenerator.Atom1Feed(
... title=u"My Weblog",
... link=u"http://www.example.com/",
... description=u"In which I write about what I ate today.",
... language=u"en")
>>> f.add_item(title=u"Hot dog today",
... link=u"http://www.example.com/entries/1/",
... description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
>>> print f.writeString('utf8')
<?xml version="1.0" encoding="utf8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
...
</feed>
.. _django/utils/feedgenerator.py: http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py
.. _Python datetime object: http://www.python.org/doc/current/lib/module-datetime.html
Custom feed generators
----------------------
If you need to produce a custom feed format, you've got a couple of options.
If the feed format is totally custom, you'll want to subclass
``SyndicationFeed`` and completely replace the ``write()`` and
``writeString()`` methods.
However, if the feed format is a spin-off of RSS or Atom (i.e. GeoRSS_, Apple's
`iTunes podcast format`_, etc.), you've got a better choice. These types of
feeds typically add extra elements and/or attributes to the underlying format,
and there are a set of methods that ``SyndicationFeed`` calls to get these extra
attributes. Thus, you can subclass the appropriate feed generator class
(``Atom1Feed`` or ``Rss201rev2Feed``) and extend these callbacks. They are:
.. _georss: http://georss.org/
.. _itunes podcast format: http://www.apple.com/itunes/store/podcaststechspecs.html
``SyndicationFeed.root_attributes(self, )``
Return a ``dict`` of attributes to add to the root feed element
(``feed``/``channel``).
``SyndicationFeed.add_root_elements(self, handler)``
Callback to add elements inside the root feed element
(``feed``/``channel``). ``handler`` is an `XMLGenerator`_ from Python's
built-in SAX library; you'll call methods on it to add to the XML
document in process.
``SyndicationFeed.item_attributes(self, item)``
Return a ``dict`` of attributes to add to each item (``item``/``entry``)
element. The argument, ``item``, is a dictionary of all the data passed to
``SyndicationFeed.add_item()``.
``SyndicationFeed.add_item_elements(self, handler, item)``
Callback to add elements to each item (``item``/``entry``) element.
``handler`` and ``item`` are as above.
.. warning::
If you override any of these methods, be sure to call the superclass methods
since they add the required elements for each feed format.
For example, you might start implementing an iTunes RSS feed generator like so::
class iTunesFeed(Rss201rev2Feed):
def root_attibutes(self):
attrs = super(iTunesFeed, self).root_attibutes()
attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd
return attrs
def add_root_elements(self, handler):
super(iTunesFeed, self).add_root_elements(handler)
handler.addQuickElement('itunes:explicit', 'clean')
Obviously there's a lot more work to be done for a complete custom feed class,
but the above example should demonstrate the basic idea.
.. _XMLGenerator: http://docs.python.org/dev/library/xml.sax.utils.html#xml.sax.saxutils.XMLGenerator

View file

@ -0,0 +1,58 @@
.. _ref-contrib-webdesign:
========================
django.contrib.webdesign
========================
.. module:: django.contrib.webdesign
:synopsis: Helpers and utilities targeted primarily at Web *designers*
rather than Web *developers*.
The ``django.contrib.webdesign`` package, part of the
:ref:`"django.contrib" add-ons <ref-contrib-index>`, provides various Django
helpers that are particularly useful to Web *designers* (as opposed to
developers).
At present, the package contains only a single template tag. If you have ideas
for Web-designer-friendly functionality in Django, please
:ref:`suggest them <internals-contributing>`.
Template tags
=============
To use these template tags, add ``'django.contrib.webdesign'`` to your
:setting:`INSTALLED_APPS` setting. Once you've done that, use
``{% load webdesign %}`` in a template to give your template access to the tags.
lorem
=====
Displays random "lorem ipsum" Latin text. This is useful for providing sample
data in templates.
Usage::
{% lorem [count] [method] [random] %}
The ``{% lorem %}`` tag can be used with zero, one, two or three arguments.
The arguments are:
=========== =============================================================
Argument Description
=========== =============================================================
``count`` A number (or variable) containing the number of paragraphs or
words to generate (default is 1).
``method`` Either ``w`` for words, ``p`` for HTML paragraphs or ``b``
for plain-text paragraph blocks (default is ``b``).
``random`` The word ``random``, which if given, does not use the common
paragraph ("Lorem ipsum dolor sit amet...") when generating
text.
=========== =============================================================
Examples:
* ``{% lorem %}`` will output the common "lorem ipsum" paragraph.
* ``{% lorem 3 p %}`` will output the common "lorem ipsum" paragraph
and two random paragraphs each wrapped in HTML ``<p>`` tags.
* ``{% lorem 2 w random %}`` will output two random Latin words.