mirror of
https://github.com/django/django.git
synced 2025-08-04 10:59:45 +00:00
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:
parent
b3688e8194
commit
97cb07c3a1
188 changed files with 19913 additions and 17059 deletions
BIN
docs/ref/contrib/_images/flatfiles_admin.png
Normal file
BIN
docs/ref/contrib/_images/flatfiles_admin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
BIN
docs/ref/contrib/_images/users_changelist.png
Normal file
BIN
docs/ref/contrib/_images/users_changelist.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
913
docs/ref/contrib/admin.txt
Normal file
913
docs/ref/contrib/admin.txt
Normal 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),
|
||||
)
|
6
docs/ref/contrib/auth.txt
Normal file
6
docs/ref/contrib/auth.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
.. _ref-contrib-auth:
|
||||
|
||||
``django.contrib.auth``
|
||||
=======================
|
||||
|
||||
See :ref:`topics-auth`.
|
347
docs/ref/contrib/contenttypes.txt
Normal file
347
docs/ref/contrib/contenttypes.txt
Normal 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
76
docs/ref/contrib/csrf.txt
Normal 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.
|
92
docs/ref/contrib/databrowse.txt
Normal file
92
docs/ref/contrib/databrowse.txt
Normal 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.
|
153
docs/ref/contrib/flatpages.txt
Normal file
153
docs/ref/contrib/flatpages.txt
Normal 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.
|
110
docs/ref/contrib/formtools/form-preview.txt
Normal file
110
docs/ref/contrib/formtools/form-preview.txt
Normal 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.
|
316
docs/ref/contrib/formtools/form-wizard.txt
Normal file
316
docs/ref/contrib/formtools/form-wizard.txt
Normal 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):
|
||||
# ...
|
12
docs/ref/contrib/formtools/index.txt
Normal file
12
docs/ref/contrib/formtools/index.txt
Normal 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
|
91
docs/ref/contrib/humanize.txt
Normal file
91
docs/ref/contrib/humanize.txt
Normal 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
199
docs/ref/contrib/index.txt
Normal 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
|
651
docs/ref/contrib/localflavor.txt
Normal file
651
docs/ref/contrib/localflavor.txt
Normal 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.
|
72
docs/ref/contrib/redirects.txt
Normal file
72
docs/ref/contrib/redirects.txt
Normal 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
|
343
docs/ref/contrib/sitemaps.txt
Normal file
343
docs/ref/contrib/sitemaps.txt
Normal 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
401
docs/ref/contrib/sites.txt
Normal 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`.
|
992
docs/ref/contrib/syndication.txt
Normal file
992
docs/ref/contrib/syndication.txt
Normal 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
|
58
docs/ref/contrib/webdesign.txt
Normal file
58
docs/ref/contrib/webdesign.txt
Normal 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.
|
Loading…
Add table
Add a link
Reference in a new issue