summaryrefslogtreecommitdiff
path: root/parts/django/docs/ref/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/docs/ref/contrib')
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/article_actions.pngbin0 -> 38545 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/article_actions_message.pngbin0 -> 22098 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.pngbin0 -> 42243 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/user_actions.pngbin0 -> 27047 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/users_changelist.pngbin0 -> 59355 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/actions.txt351
-rw-r--r--parts/django/docs/ref/contrib/admin/admindocs.txt161
-rw-r--r--parts/django/docs/ref/contrib/admin/index.txt1613
-rw-r--r--parts/django/docs/ref/contrib/auth.txt4
-rw-r--r--parts/django/docs/ref/contrib/comments/custom.txt202
-rw-r--r--parts/django/docs/ref/contrib/comments/example.txt208
-rw-r--r--parts/django/docs/ref/contrib/comments/forms.txt46
-rw-r--r--parts/django/docs/ref/contrib/comments/index.txt302
-rw-r--r--parts/django/docs/ref/contrib/comments/models.txt80
-rw-r--r--parts/django/docs/ref/contrib/comments/moderation.txt230
-rw-r--r--parts/django/docs/ref/contrib/comments/settings.txt33
-rw-r--r--parts/django/docs/ref/contrib/comments/signals.txt91
-rw-r--r--parts/django/docs/ref/contrib/comments/upgrade.txt78
-rw-r--r--parts/django/docs/ref/contrib/contenttypes.txt385
-rw-r--r--parts/django/docs/ref/contrib/csrf.txt433
-rw-r--r--parts/django/docs/ref/contrib/databrowse.txt90
-rw-r--r--parts/django/docs/ref/contrib/flatpages.txt167
-rw-r--r--parts/django/docs/ref/contrib/formtools/form-preview.txt121
-rw-r--r--parts/django/docs/ref/contrib/formtools/form-wizard.txt312
-rw-r--r--parts/django/docs/ref/contrib/formtools/index.txt10
-rw-r--r--parts/django/docs/ref/contrib/gis/admin.txt72
-rw-r--r--parts/django/docs/ref/contrib/gis/commands.txt83
-rwxr-xr-xparts/django/docs/ref/contrib/gis/create_template_postgis-1.3.sh9
-rwxr-xr-xparts/django/docs/ref/contrib/gis/create_template_postgis-1.4.sh9
-rwxr-xr-xparts/django/docs/ref/contrib/gis/create_template_postgis-1.5.sh10
-rwxr-xr-xparts/django/docs/ref/contrib/gis/create_template_postgis-debian.sh9
-rw-r--r--parts/django/docs/ref/contrib/gis/db-api.txt349
-rw-r--r--parts/django/docs/ref/contrib/gis/deployment.txt99
-rw-r--r--parts/django/docs/ref/contrib/gis/feeds.txt95
-rw-r--r--parts/django/docs/ref/contrib/gis/gdal.txt1114
-rw-r--r--parts/django/docs/ref/contrib/gis/geoip.txt223
-rw-r--r--parts/django/docs/ref/contrib/gis/geoquerysets.txt1256
-rw-r--r--parts/django/docs/ref/contrib/gis/geos.txt911
-rw-r--r--parts/django/docs/ref/contrib/gis/index.txt33
-rw-r--r--parts/django/docs/ref/contrib/gis/install.txt1190
-rw-r--r--parts/django/docs/ref/contrib/gis/layermapping.txt220
-rw-r--r--parts/django/docs/ref/contrib/gis/measure.txt180
-rw-r--r--parts/django/docs/ref/contrib/gis/model-api.txt265
-rw-r--r--parts/django/docs/ref/contrib/gis/ogrinspect.txt21
-rw-r--r--parts/django/docs/ref/contrib/gis/sitemaps.txt27
-rw-r--r--parts/django/docs/ref/contrib/gis/testing.txt268
-rw-r--r--parts/django/docs/ref/contrib/gis/tutorial.txt758
-rw-r--r--parts/django/docs/ref/contrib/gis/utils.txt32
-rw-r--r--parts/django/docs/ref/contrib/humanize.txt100
-rw-r--r--parts/django/docs/ref/contrib/index.txt207
-rw-r--r--parts/django/docs/ref/contrib/localflavor.txt842
-rw-r--r--parts/django/docs/ref/contrib/markup.txt42
-rw-r--r--parts/django/docs/ref/contrib/messages.txt411
-rw-r--r--parts/django/docs/ref/contrib/redirects.txt70
-rw-r--r--parts/django/docs/ref/contrib/sitemaps.txt351
-rw-r--r--parts/django/docs/ref/contrib/sites.txt415
-rw-r--r--parts/django/docs/ref/contrib/syndication.txt949
-rw-r--r--parts/django/docs/ref/contrib/webdesign.txt56
58 files changed, 15593 insertions, 0 deletions
diff --git a/parts/django/docs/ref/contrib/admin/_images/article_actions.png b/parts/django/docs/ref/contrib/admin/_images/article_actions.png
new file mode 100644
index 0000000..78a78ae
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/article_actions.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png b/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png
new file mode 100644
index 0000000..6ea9439
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png
new file mode 100644
index 0000000..391a629
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/user_actions.png b/parts/django/docs/ref/contrib/admin/_images/user_actions.png
new file mode 100644
index 0000000..fdbe2ad
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/user_actions.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/users_changelist.png b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png
new file mode 100644
index 0000000..d5f9c01
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/actions.txt b/parts/django/docs/ref/contrib/admin/actions.txt
new file mode 100644
index 0000000..0fab59e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/actions.txt
@@ -0,0 +1,351 @@
+=============
+Admin actions
+=============
+
+.. versionadded:: 1.1
+
+.. currentmodule:: django.contrib.admin
+
+The basic workflow of Django's admin is, in a nutshell, "select an object,
+then change it." This works well for a majority of use cases. However, if you
+need to make the same change to many objects at once, this workflow can be
+quite tedious.
+
+In these cases, Django's admin lets you write and register "actions" -- simple
+functions that get called with a list of objects selected on the change list
+page.
+
+If you look at any change list in the admin, you'll see this feature in
+action; Django ships with a "delete selected objects" action available to all
+models. For example, here's the user module from Django's built-in
+:mod:`django.contrib.auth` app:
+
+.. image:: _images/user_actions.png
+
+.. warning::
+
+ The "delete selected objects" action uses :meth:`QuerySet.delete()
+ <django.db.models.QuerySet.delete>` for efficiency reasons, which has an
+ important caveat: your model's ``delete()`` method will not be called.
+
+ If you wish to override this behavior, simply write a custom action which
+ accomplishes deletion in your preferred manner -- for example, by calling
+ ``Model.delete()`` for each of the selected items.
+
+ For more background on bulk deletion, see the documentation on :ref:`object
+ deletion <topics-db-queries-delete>`.
+
+Read on to find out how to add your own actions to this list.
+
+Writing actions
+===============
+
+The easiest way to explain actions is by example, so let's dive in.
+
+A common use case for admin actions is the bulk updating of a model. Imagine a
+simple news application with an ``Article`` model::
+
+ from django.db import models
+
+ STATUS_CHOICES = (
+ ('d', 'Draft'),
+ ('p', 'Published'),
+ ('w', 'Withdrawn'),
+ )
+
+ class Article(models.Model):
+ title = models.CharField(max_length=100)
+ body = models.TextField()
+ status = models.CharField(max_length=1, choices=STATUS_CHOICES)
+
+ def __unicode__(self):
+ return self.title
+
+A common task we might perform with a model like this is to update an
+article's status from "draft" to "published". We could easily do this in the
+admin one article at a time, but if we wanted to bulk-publish a group of
+articles, it'd be tedious. So, let's write an action that lets us change an
+article's status to "published."
+
+Writing action functions
+------------------------
+
+First, we'll need to write a function that gets called when the action is
+trigged from the admin. Action functions are just regular functions that take
+three arguments:
+
+ * The current :class:`ModelAdmin`
+ * An :class:`~django.http.HttpRequest` representing the current request,
+ * A :class:`~django.db.models.QuerySet` containing the set of objects
+ selected by the user.
+
+Our publish-these-articles function won't need the :class:`ModelAdmin` or the
+request object, but we will use the queryset::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+
+.. note::
+
+ For the best performance, we're using the queryset's :ref:`update method
+ <topics-db-queries-update>`. Other types of actions might need to deal
+ with each object individually; in these cases we'd just iterate over the
+ queryset::
+
+ for obj in queryset:
+ do_something_with(obj)
+
+That's actually all there is to writing an action! However, we'll take one
+more optional-but-useful step and give the action a "nice" title in the admin.
+By default, this action would appear in the action list as "Make published" --
+the function name, with underscores replaced by spaces. That's fine, but we
+can provide a better, more human-friendly name by giving the
+``make_published`` function a ``short_description`` attribute::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+.. note::
+
+ This might look familiar; the admin's ``list_display`` option uses the
+ same technique to provide human-readable descriptions for callback
+ functions registered there, too.
+
+Adding actions to the :class:`ModelAdmin`
+-----------------------------------------
+
+Next, we'll need to inform our :class:`ModelAdmin` of the action. This works
+just like any other configuration option. So, the complete ``admin.py`` with
+the action and its registration would look like::
+
+ from django.contrib import admin
+ from myapp.models import Article
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+ class ArticleAdmin(admin.ModelAdmin):
+ list_display = ['title', 'status']
+ ordering = ['title']
+ actions = [make_published]
+
+ admin.site.register(Article, ArticleAdmin)
+
+That code will give us an admin change list that looks something like this:
+
+.. image:: _images/article_actions.png
+
+That's really all there is to it! If you're itching to write your own actions,
+you now know enough to get started. The rest of this document just covers more
+advanced techniques.
+
+Advanced action techniques
+==========================
+
+There's a couple of extra options and possibilities you can exploit for more
+advanced options.
+
+Actions as :class:`ModelAdmin` methods
+--------------------------------------
+
+The example above shows the ``make_published`` action defined as a simple
+function. That's perfectly fine, but it's not perfect from a code design point
+of view: since the action is tightly coupled to the ``Article`` object, it
+makes sense to hook the action to the ``ArticleAdmin`` object itself.
+
+That's easy enough to do::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ actions = ['make_published']
+
+ def make_published(self, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+Notice first that we've moved ``make_published`` into a method and renamed the
+`modeladmin` parameter to `self`, and second that we've now put the string
+``'make_published'`` in ``actions`` instead of a direct function reference. This
+tells the :class:`ModelAdmin` to look up the action as a method.
+
+Defining actions as methods gives the action more straightforward, idiomatic
+access to the :class:`ModelAdmin` itself, allowing the action to call any of the
+methods provided by the admin.
+
+.. _custom-admin-action:
+
+For example, we can use ``self`` to flash a message to the user informing her
+that the action was successful::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ def make_published(self, request, queryset):
+ rows_updated = queryset.update(status='p')
+ if rows_updated == 1:
+ message_bit = "1 story was"
+ else:
+ message_bit = "%s stories were" % rows_updated
+ self.message_user(request, "%s successfully marked as published." % message_bit)
+
+This make the action match what the admin itself does after successfully
+performing an action:
+
+.. image:: _images/article_actions_message.png
+
+Actions that provide intermediate pages
+---------------------------------------
+
+By default, after an action is performed the user is simply redirected back
+to the original change list page. However, some actions, especially more
+complex ones, will need to return intermediate pages. For example, the
+built-in delete action asks for confirmation before deleting the selected
+objects.
+
+To provide an intermediary page, simply return an
+:class:`~django.http.HttpResponse` (or subclass) from your action. For
+example, you might write a simple export function that uses Django's
+:doc:`serialization functions </topics/serialization>` to dump some selected
+objects as JSON::
+
+ from django.http import HttpResponse
+ from django.core import serializers
+
+ def export_as_json(modeladmin, request, queryset):
+ response = HttpResponse(mimetype="text/javascript")
+ serializers.serialize("json", queryset, stream=response)
+ return response
+
+Generally, something like the above isn't considered a great idea. Most of the
+time, the best practice will be to return an
+:class:`~django.http.HttpResponseRedirect` and redirect the user to a view
+you've written, passing the list of selected objects in the GET query string.
+This allows you to provide complex interaction logic on the intermediary
+pages. For example, if you wanted to provide a more complete export function,
+you'd want to let the user choose a format, and possibly a list of fields to
+include in the export. The best thing to do would be to write a small action
+that simply redirects to your custom export view::
+
+ from django.contrib import admin
+ from django.contrib.contenttypes.models import ContentType
+ from django.http import HttpResponseRedirect
+
+ def export_selected_objects(modeladmin, request, queryset):
+ selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
+ ct = ContentType.objects.get_for_model(queryset.model)
+ return HttpResponseRedirect("/export/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
+
+As you can see, the action is the simple part; all the complex logic would
+belong in your export view. This would need to deal with objects of any type,
+hence the business with the ``ContentType``.
+
+Writing this view is left as an exercise to the reader.
+
+.. _adminsite-actions:
+
+Making actions available site-wide
+----------------------------------
+
+.. method:: AdminSite.add_action(action[, name])
+
+ Some actions are best if they're made available to *any* object in the admin
+ site -- the export action defined above would be a good candidate. You can
+ make an action globally available using :meth:`AdminSite.add_action()`. For
+ example::
+
+ from django.contrib import admin
+
+ admin.site.add_action(export_selected_objects)
+
+ This makes the `export_selected_objects` action globally available as an
+ action named `"export_selected_objects"`. You can explicitly give the action
+ a name -- good if you later want to programatically :ref:`remove the action
+ <disabling-admin-actions>` -- by passing a second argument to
+ :meth:`AdminSite.add_action()`::
+
+ admin.site.add_action(export_selected_objects, 'export_selected')
+
+.. _disabling-admin-actions:
+
+Disabling actions
+-----------------
+
+Sometimes you need to disable certain actions -- especially those
+:ref:`registered site-wide <adminsite-actions>` -- for particular objects.
+There's a few ways you can disable actions:
+
+Disabling a site-wide action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: AdminSite.disable_action(name)
+
+ If you need to disable a :ref:`site-wide action <adminsite-actions>` you can
+ call :meth:`AdminSite.disable_action()`.
+
+ For example, you can use this method to remove the built-in "delete selected
+ objects" action::
+
+ admin.site.disable_action('delete_selected')
+
+ Once you've done the above, that action will no longer be available
+ site-wide.
+
+ If, however, you need to re-enable a globally-disabled action for one
+ particular model, simply list it explicitly in your ``ModelAdmin.actions``
+ list::
+
+ # Globally disable delete selected
+ admin.site.disable_action('delete_selected')
+
+ # This ModelAdmin will not have delete_selected available
+ class SomeModelAdmin(admin.ModelAdmin):
+ actions = ['some_other_action']
+ ...
+
+ # This one will
+ class AnotherModelAdmin(admin.ModelAdmin):
+ actions = ['delete_selected', 'a_third_action']
+ ...
+
+
+Disabling all actions for a particular :class:`ModelAdmin`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want *no* bulk actions available for a given :class:`ModelAdmin`, simply
+set :attr:`ModelAdmin.actions` to ``None``::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ actions = None
+
+This tells the :class:`ModelAdmin` to not display or allow any actions,
+including any :ref:`site-wide actions <adminsite-actions>`.
+
+Conditionally enabling or disabling actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: ModelAdmin.get_actions(request)
+
+ Finally, you can conditionally enable or disable actions on a per-request
+ (and hence per-user basis) by overriding :meth:`ModelAdmin.get_actions`.
+
+ This returns a dictionary of actions allowed. The keys are action names, and
+ the values are ``(function, name, short_description)`` tuples.
+
+ Most of the time you'll use this method to conditionally remove actions from
+ the list gathered by the superclass. For example, if I only wanted users
+ whose names begin with 'J' to be able to delete objects in bulk, I could do
+ the following::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ ...
+
+ def get_actions(self, request):
+ actions = super(MyModelAdmin, self).get_actions(request)
+ if request.user.username[0].upper() != 'J':
+ del actions['delete_selected']
+ return actions
+
+
diff --git a/parts/django/docs/ref/contrib/admin/admindocs.txt b/parts/django/docs/ref/contrib/admin/admindocs.txt
new file mode 100644
index 0000000..6743921
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/admindocs.txt
@@ -0,0 +1,161 @@
+========================================
+The Django admin documentation generator
+========================================
+
+.. module:: django.contrib.admindocs
+ :synopsis: Django's admin documentation generator.
+
+.. currentmodule:: django.contrib.admindocs
+
+Django's :mod:`~django.contrib.admindocs` app pulls documentation from the
+docstrings of models, views, template tags, and template filters for any app in
+:setting:`INSTALLED_APPS` and makes that documentation available from the
+:mod:`Django admin <django.contrib.admin>`.
+
+In addition to providing offline documentation for all template tags and
+template filters that ship with Django, you may utilize admindocs to quickly
+document your own code.
+
+Overview
+========
+
+To activate the :mod:`~django.contrib.admindocs`, you will need to do
+the following:
+
+ * Add :mod:`django.contrib.admindocs` to your :setting:`INSTALLED_APPS`.
+ * Add ``(r'^admin/doc/', include('django.contrib.admindocs.urls'))`` to
+ your :data:`urlpatterns`. Make sure it's included *before* the
+ ``r'^admin/'`` entry, so that requests to ``/admin/doc/`` don't get
+ handled by the latter entry.
+ * Install the docutils Python module (http://docutils.sf.net/).
+ * **Optional:** Linking to templates requires the :setting:`ADMIN_FOR`
+ setting to be configured.
+ * **Optional:** Using the admindocs bookmarklets requires the
+ :mod:`XViewMiddleware<django.middleware.doc>` to be installed.
+
+Once those steps are complete, you can start browsing the documentation by
+going to your admin interface and clicking the "Documentation" link in the
+upper right of the page.
+
+Documentation helpers
+=====================
+
+The following special markup can be used in your docstrings to easily create
+hyperlinks to other components:
+
+================= =======================
+Django Component reStructuredText roles
+================= =======================
+Models ``:model:`appname.ModelName```
+Views ``:view:`appname.view_name```
+Template tags ``:tag:`tagname```
+Template filters ``:filter:`filtername```
+Templates ``:template:`path/to/template.html```
+================= =======================
+
+Model reference
+===============
+
+The **models** section of the ``admindocs`` page describes each model in the
+system along with all the fields and methods available on it. Relationships to
+other models appear as hyperlinks. Descriptions are pulled from ``help_text``
+attributes on fields or from docstrings on model methods.
+
+A model with useful documentation might look like this::
+
+ class BlogEntry(models.Model):
+ """
+ Stores a single blog entry, related to :model:`blog.Blog` and
+ :model:`auth.User`.
+
+ """
+ slug = models.SlugField(help_text="A short label, generally used in URLs.")
+ author = models.ForeignKey(User)
+ blog = models.ForeignKey(Blog)
+ ...
+
+ def publish(self):
+ """Makes the blog entry live on the site."""
+ ...
+
+View reference
+==============
+
+Each URL in your site has a separate entry in the ``admindocs`` page, and
+clicking on a given URL will show you the corresponding view. Helpful things
+you can document in your view function docstrings include:
+
+ * A short description of what the view does.
+ * The **context**, or a list of variables available in the view's template.
+ * The name of the template or templates that are used for that view.
+
+For example::
+
+ from myapp.models import MyModel
+
+ def my_view(request, slug):
+ """
+ Display an individual :model:`myapp.MyModel`.
+
+ **Context**
+
+ ``RequestContext``
+
+ ``mymodel``
+ An instance of :model:`myapp.MyModel`.
+
+ **Template:**
+
+ :template:`myapp/my_template.html`
+
+ """
+ return render_to_response('myapp/my_template.html', {
+ 'mymodel': MyModel.objects.get(slug=slug)
+ }, context_instance=RequestContext(request))
+
+
+Template tags and filters reference
+===================================
+
+The **tags** and **filters** ``admindocs`` sections describe all the tags and
+filters that come with Django (in fact, the :ref:`built-in tag reference
+<ref-templates-builtins-tags>` and :ref:`built-in filter reference
+<ref-templates-builtins-filters>` documentation come directly from those
+pages). Any tags or filters that you create or are added by a third-party app
+will show up in these sections as well.
+
+
+Template reference
+==================
+
+While ``admindocs`` does not include a place to document templates by
+themselves, if you use the ``:template:`path/to/template.html``` syntax in a
+docstring the resulting page will verify the path of that template with
+Django's :ref:`template loaders <template-loaders>`. This can be a handy way to
+check if the specified template exists and to show where on the filesystem that
+template is stored.
+
+
+Included Bookmarklets
+=====================
+
+Several useful bookmarklets are available from the ``admindocs`` page:
+
+ Documentation for this page
+ Jumps you from any page to the documentation for the view that generates
+ that page.
+
+ Show object ID
+ Shows the content-type and unique ID for pages that represent a single
+ object.
+
+ Edit this object
+ Jumps to the admin page for pages that represent a single object.
+
+Using these bookmarklets requires that you are either logged into the
+:mod:`Django admin <django.contrib.admin>` as a
+:class:`~django.contrib.auth.models.User` with
+:attr:`~django.contrib.auth.models.User.is_staff` set to `True`, or
+that the :mod:`django.middleware.doc` middleware and
+:mod:`XViewMiddleware <django.middleware.doc>` are installed and you
+are accessing the site from an IP address listed in :setting:`INTERNAL_IPS`.
diff --git a/parts/django/docs/ref/contrib/admin/index.txt b/parts/django/docs/ref/contrib/admin/index.txt
new file mode 100644
index 0000000..b99cfdc
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/index.txt
@@ -0,0 +1,1613 @@
+=====================
+The Django admin site
+=====================
+
+.. module:: django.contrib.admin
+ :synopsis: Django's 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 six steps in activating the Django admin site:
+
+ 1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS`
+ setting.
+
+ 2. Admin has two dependencies - ``django.contrib.auth`` and
+ ``django.contrib.contenttypes``. If these applications are not
+ in your :setting:`INSTALLED_APPS` list, add them.
+
+ 3. Determine which of your application's models should be editable in the
+ admin interface.
+
+ 4. For each of those models, optionally create a ``ModelAdmin`` class that
+ encapsulates the customized admin functionality and options for that
+ particular model.
+
+ 5. Instantiate an ``AdminSite`` and tell it about each of your models and
+ ``ModelAdmin`` classes.
+
+ 6. Hook the ``AdminSite`` instance into your URLconf.
+
+Other topics
+------------
+
+.. toctree::
+ :maxdepth: 1
+
+ actions
+ admindocs
+
+.. seealso::
+
+ For information about serving the media files (images, JavaScript, and CSS)
+ associated with the admin in production, see :ref:`serving-media-files`.
+
+``ModelAdmin`` objects
+======================
+
+.. class:: ModelAdmin
+
+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 of the ``ModelAdmin``::
+
+ from django.contrib import admin
+ from myproject.myapp.models import Author
+
+ class AuthorAdmin(admin.ModelAdmin):
+ pass
+ admin.site.register(Author, AuthorAdmin)
+
+.. admonition:: Do you need a ``ModelAdmin`` object at all?
+
+ In the preceding example, the ``ModelAdmin`` class doesn't define any
+ custom values (yet). As a result, the default admin interface will be
+ provided. If you are happy with the default admin interface, you don't
+ need to define a ``ModelAdmin`` object at all -- you can register the
+ model class without providing a ``ModelAdmin`` description. The
+ preceding example could be simplified to::
+
+ from django.contrib import admin
+ from myproject.myapp.models import Author
+
+ admin.site.register(Author)
+
+``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'
+
+.. attribute:: ModelAdmin.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'
+
+.. attribute:: ModelAdmin.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`_.
+
+.. attribute:: ModelAdmin.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'),
+ }
+
+ .. versionadded:: 1.2
+
+ ``fields`` can contain values defined in
+ :attr:`ModelAdmin.readonly_fields` to be displayed as read-only.
+
+ * ``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.
+
+.. attribute:: ModelAdmin.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.
+
+.. versionadded:: 1.2
+
+``fields`` can contain values defined in :attr:`ModelAdmin.readonly_fields`
+to be displayed as read-only.
+
+.. 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.
+
+.. attribute:: ModelAdmin.exclude
+
+This attribute, if given, should be a list of field names to exclude from the
+form.
+
+For example, let's consider the following model::
+
+ class Author(models.Model):
+ name = models.CharField(max_length=100)
+ title = models.CharField(max_length=3)
+ birth_date = models.DateField(blank=True, null=True)
+
+If you want a form for the ``Author`` model that includes only the ``name``
+and ``title`` fields, you would specify ``fields`` or ``exclude`` like this::
+
+ class AuthorAdmin(admin.ModelAdmin):
+ fields = ('name', 'title')
+
+ class AuthorAdmin(admin.ModelAdmin):
+ exclude = ('birth_date',)
+
+Since the Author model only has three fields, ``name``, ``title``, and
+``birth_date``, the forms resulting from the above declarations will contain
+exactly the same fields.
+
+.. attribute:: ModelAdmin.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.
+
+.. attribute:: ModelAdmin.filter_vertical
+
+Same as ``filter_horizontal``, but is a vertical display of the filter
+interface.
+
+.. attribute:: ModelAdmin.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
+ 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] == '195'
+ 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.
+
+.. attribute:: ModelAdmin.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')
+
+.. _admin-list-editable:
+
+.. attribute:: ModelAdmin.list_editable
+
+.. versionadded:: 1.1
+
+Set ``list_editable`` to a list of field names on the model which will allow
+editing on the change list page. That is, fields listed in ``list_editable``
+will be displayed as form widgets on the change list page, allowing users to
+edit and save multiple rows at once.
+
+.. note::
+
+ ``list_editable`` interacts with a couple of other options in particular
+ ways; you should note the following rules:
+
+ * Any field in ``list_editable`` must also be in ``list_display``. You
+ can't edit a field that's not displayed!
+
+ * The same field can't be listed in both ``list_editable`` and
+ ``list_display_links`` -- a field can't be both a form and a link.
+
+ You'll get a validation error if either of these rules are broken.
+
+.. attribute:: ModelAdmin.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.)
+
+.. attribute:: ModelAdmin.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``.
+
+.. attribute:: ModelAdmin.list_select_related
+
+Set ``list_select_related`` to tell Django to use
+:meth:`~django.db.models.QuerySet.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 :meth:`~django.db.models.QuerySet.select_related`,
+regardless of this setting, if one of the ``list_display`` fields is a
+``ForeignKey``.
+
+.. attribute:: ModelAdmin.inlines
+
+See ``InlineModelAdmin`` objects below.
+
+.. attribute:: ModelAdmin.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.
+
+.. attribute:: ModelAdmin.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.
+
+.. attribute:: ModelAdmin.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.
+
+.. attribute:: ModelAdmin.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",)
+
+.. attribute:: ModelAdmin.readonly_fields
+
+.. versionadded:: 1.2
+
+By default the admin shows all fields as editable. Any fields in this option
+(which should be a ``list`` or ``tuple``) will display its data as-is and
+non-editable. This option behaves nearly identical to :attr:`ModelAdmin.list_display`.
+Usage is the same, however, when you specify :attr:`ModelAdmin.fields` or
+:attr:`ModelAdmin.fieldsets` the read-only fields must be present to be shown
+(they are ignored otherwise).
+
+If ``readonly_fields`` is used without defining explicit ordering through
+:attr:`ModelAdmin.fields` or :attr:`ModelAdmin.fieldsets` they will be added
+last after all editable fields.
+
+.. attribute:: ModelAdmin.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``.
+
+.. attribute:: ModelAdmin.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``.
+
+.. attribute:: ModelAdmin.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`` or
+``ManyToManyField`` with the lookup API "follow" notation::
+
+ search_fields = ['foreign_key__related_fieldname']
+
+For example, if you have a blog entry with an author, the following definition
+would enable search blog entries by the email address of the author::
+
+ search_fields = ['user__email']
+
+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.
+
+.. attribute:: ModelAdmin.formfield_overrides
+
+.. versionadded:: 1.1
+
+This provides a quick-and-dirty way to override some of the
+:class:`~django.forms.Field` options for use in the admin.
+``formfield_overrides`` is a dictionary mapping a field class to a dict of
+arguments to pass to the field at construction time.
+
+Since that's a bit abstract, let's look at a concrete example. The most common
+use of ``formfield_overrides`` is to add a custom widget for a certain type of
+field. So, imagine we've written a ``RichTextEditorWidget`` that we'd like to
+use for large text fields instead of the default ``<textarea>``. Here's how we'd
+do that::
+
+ from django.db import models
+ from django.contrib import admin
+
+ # Import our custom widget and our model from where they're defined
+ from myapp.widgets import RichTextEditorWidget
+ from myapp.models import MyModel
+
+ class MyModelAdmin(admin.ModelAdmin):
+ formfield_overrides = {
+ models.TextField: {'widget': RichTextEditorWidget},
+ }
+
+Note that the key in the dictionary is the actual field class, *not* a string.
+The value is another dictionary; these arguments will be passed to
+:meth:`~django.forms.Field.__init__`. See :doc:`/ref/forms/api` for details.
+
+.. warning::
+
+ If you want to use a custom widget with a relation field (i.e.
+ :class:`~django.db.models.ForeignKey` or
+ :class:`~django.db.models.ManyToManyField`), make sure you haven't included
+ that field's name in ``raw_id_fields`` or ``radio_fields``.
+
+ ``formfield_overrides`` won't let you change the widget on relation fields
+ that have ``raw_id_fields`` or ``radio_fields`` set. That's because
+ ``raw_id_fields`` and ``radio_fields`` imply custom widgets of their own.
+
+.. attribute:: ModelAdmin.actions
+
+.. versionadded:: 1.1
+
+A list of actions to make available on the change list page. See
+:doc:`/ref/contrib/admin/actions` for details.
+
+.. attribute:: ModelAdmin.actions_on_top
+.. attribute:: ModelAdmin.actions_on_bottom
+
+.. versionadded:: 1.1
+
+Controls where on the page the actions bar appears. By default, the admin
+changelist displays actions at the top of the page (``actions_on_top = True;
+actions_on_bottom = False``).
+
+.. attribute:: ModelAdmin.actions_selection_counter
+
+.. versionadded:: 1.2
+
+Controls whether a selection counter is display next to the action dropdown.
+By default, the admin changelist will display it
+(``actions_selection_counter = True``).
+
+Custom template options
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The `Overriding Admin Templates`_ section describes how to override or extend
+the default admin templates. Use the following options to override the default
+templates used by the :class:`ModelAdmin` views:
+
+.. attribute:: ModelAdmin.add_form_template
+
+ .. versionadded:: 1.2
+
+ Path to a custom template, used by :meth:`add_view`.
+
+.. attribute:: ModelAdmin.change_form_template
+
+ Path to a custom template, used by :meth:`change_view`.
+
+.. attribute:: ModelAdmin.change_list_template
+
+ Path to a custom template, used by :meth:`changelist_view`.
+
+.. attribute:: ModelAdmin.delete_confirmation_template
+
+ Path to a custom template, used by :meth:`delete_view` for displaying a
+ confirmation page when deleting one or more objects.
+
+.. attribute:: ModelAdmin.delete_selected_confirmation_template
+
+ .. versionadded:: 1.2
+
+ Path to a custom template, used by the :meth:`delete_selected`
+ action method for displaying a confirmation page when deleting one
+ or more objects. See the :doc:`actions
+ documentation</ref/contrib/admin/actions>`.
+
+.. attribute:: ModelAdmin.object_history_template
+
+ Path to a custom template, used by :meth:`history_view`.
+
+
+.. _model-admin-methods:
+
+``ModelAdmin`` methods
+----------------------
+
+.. method:: ModelAdmin.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()
+
+.. method:: ModelAdmin.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()
+
+.. method:: ModelAdmin.get_readonly_fields(self, request, obj=None)
+
+.. versionadded:: 1.2
+
+The ``get_readonly_fields`` method is given the ``HttpRequest`` and the
+``obj`` being edited (or ``None`` on an add form) and is expected to return a
+``list`` or ``tuple`` of field names that will be displayed as read-only, as
+described above in the :attr:`ModelAdmin.readonly_fields` section.
+
+.. method:: ModelAdmin.get_urls(self)
+
+.. versionadded:: 1.1
+
+The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for
+that ModelAdmin in the same way as a URLconf. Therefore you can extend them as
+documented in :doc:`/topics/http/urls`::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def get_urls(self):
+ urls = super(MyModelAdmin, self).get_urls()
+ my_urls = patterns('',
+ (r'^my_view/$', self.my_view)
+ )
+ return my_urls + urls
+
+.. note::
+
+ Notice that the custom patterns are included *before* the regular admin
+ URLs: the admin URL patterns are very permissive and will match nearly
+ anything, so you'll usually want to prepend your custom URLs to the built-in
+ ones.
+
+However, the ``self.my_view`` function registered above suffers from two
+problems:
+
+ * It will *not* perform any permission checks, so it will be accessible to
+ the general public.
+ * It will *not* provide any header details to prevent caching. This means if
+ the page retrieves data from the database, and caching middleware is
+ active, the page could show outdated information.
+
+Since this is usually not what you want, Django provides a convenience wrapper
+to check permissions and mark the view as non-cacheable. This wrapper is
+:meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a
+``ModelAdmin`` instance); use it like so::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def get_urls(self):
+ urls = super(MyModelAdmin, self).get_urls()
+ my_urls = patterns('',
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view))
+ )
+ return my_urls + urls
+
+Notice the wrapped view in the fifth line above::
+
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view))
+
+This wrapping will protect ``self.my_view`` from unauthorized access and will
+apply the ``django.views.decorators.cache.never_cache`` decorator to make sure
+it is not cached if the cache middleware is active.
+
+If the page is cacheable, but you still want the permission check to be performed,
+you can pass a ``cacheable=True`` argument to :meth:`AdminSite.admin_view`::
+
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
+
+.. method:: ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)
+
+.. versionadded:: 1.1
+
+The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
+override the default formfield for a foreign key field. For example, to
+return a subset of objects for this foreign key field based on the user::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def formfield_for_foreignkey(self, db_field, request, **kwargs):
+ if db_field.name == "car":
+ kwargs["queryset"] = Car.objects.filter(owner=request.user)
+ return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+This uses the ``HttpRequest`` instance to filter the ``Car`` foreign key field
+to only display the cars owned by the ``User`` instance.
+
+.. method:: ModelAdmin.formfield_for_manytomany(self, db_field, request, **kwargs)
+
+.. versionadded:: 1.1
+
+Like the ``formfield_for_foreignkey`` method, the ``formfield_for_manytomany``
+method can be overridden to change the default formfield for a many to many
+field. For example, if an owner can own multiple cars and cars can belong
+to multiple owners -- a many to many relationship -- you could filter the
+``Car`` foreign key field to only display the cars owned by the ``User``::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ if db_field.name == "cars":
+ kwargs["queryset"] = Car.objects.filter(owner=request.user)
+ return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
+
+.. method:: ModelAdmin.queryset(self, request)
+
+The ``queryset`` method on a ``ModelAdmin`` returns a
+:class:`~django.db.models.QuerySet` of all model instances that can be
+edited by the admin site. One use case for overriding this method is
+to show objects owned by the logged-in user::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def queryset(self, request):
+ qs = super(MyModelAdmin, self).queryset(request)
+ if request.user.is_superuser:
+ return qs
+ return qs.filter(author=request.user)
+
+.. method:: ModelAdmin.message_user(request, message)
+
+ Sends a message to the user. The default implementation creates a message
+ using the :mod:`django.contrib.messages` backend. See the
+ :ref:`custom ModelAdmin example <custom-admin-action>`.
+
+Other methods
+~~~~~~~~~~~~~
+
+.. method:: ModelAdmin.add_view(self, request, form_url='', extra_context=None)
+
+Django view for the model instance addition page. See note below.
+
+.. method:: ModelAdmin.change_view(self, request, object_id, extra_context=None)
+
+Django view for the model instance edition page. See note below.
+
+.. method:: ModelAdmin.changelist_view(self, request, extra_context=None)
+
+Django view for the model instances change list/actions page. See note below.
+
+.. method:: ModelAdmin.delete_view(self, request, object_id, extra_context=None)
+
+Django view for the model instance(s) deletion confirmation page. See note below.
+
+.. method:: ModelAdmin.history_view(self, request, object_id, extra_context=None)
+
+Django view for the page that shows the modification history for a given model
+instance.
+
+Unlike the hook-type ``ModelAdmin`` methods detailed in the previous section,
+these five methods are in reality designed to be invoked as Django views from
+the admin application URL dispatching handler to render the pages that deal
+with model instances CRUD operations. As a result, completely overriding these
+methods will significantly change the behavior of the admin application.
+
+One common reason for overriding these methods is to augment the context data
+that is provided to the template that renders the view. In the following
+example, the change view is overridden so that the rendered template is
+provided some extra mapping data that would not otherwise be available::
+
+ class MyModelAdmin(admin.ModelAdmin):
+
+ # A template for a very customized change view:
+ change_form_template = 'admin/myapp/extras/openstreetmap_change_form.html'
+
+ def get_osm_info(self):
+ # ...
+
+ def change_view(self, request, object_id, extra_context=None):
+ my_context = {
+ 'osm_data': self.get_osm_info(),
+ }
+ return super(MyModelAdmin, self).change_view(request, object_id,
+ extra_context=my_context)
+
+``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 :doc:`regular media definitions on forms </topics/forms/media>`.
+
+Django admin Javascript makes use of the `jQuery`_ library. To avoid
+conflict with user scripts, Django's jQuery is namespaced as
+``django.jQuery``. If you want to use jQuery in your own admin
+JavaScript without including a second copy, you can use the
+``django.jQuery`` object on changelist and add/edit views.
+
+.. _jQuery: http://jquery.com
+
+Adding custom validation to the admin
+-------------------------------------
+
+Adding custom validation of data in the admin is quite easy. The automatic admin
+interface 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
+:doc:`forms </ref/forms/index>` documentation on :doc:`custom validation
+</ref/forms/validation>` and, more specifically, the
+:ref:`model form validation notes <overriding-modelform-clean-method>` for more
+information.
+
+.. _admin-inlines:
+
+``InlineModelAdmin`` objects
+============================
+
+.. class:: InlineModelAdmin
+
+The admin interface has the ability to edit models on the same page as a
+parent model. These are called inlines. Suppose you have these two models::
+
+ class Author(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Book(models.Model):
+ author = models.ForeignKey(Author)
+ title = models.CharField(max_length=100)
+
+You can edit the books authored by an author on the author page. You add
+inlines to a model by specifying them in a ``ModelAdmin.inlines``::
+
+ 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:
+
+.. attribute:: InlineModelAdmin.model
+
+ The model in which the inline is using. This is required.
+
+.. attribute:: InlineModelAdmin.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.
+
+.. attribute:: InlineModelAdmin.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>`.
+
+.. attribute:: InlineModelAdmin.form
+
+ The value for ``form`` defaults to ``ModelForm``. This is what is passed
+ through to ``inlineformset_factory`` when creating the formset for this
+ inline.
+
+.. _ref-contrib-admin-inline-extra:
+
+.. attribute:: InlineModelAdmin.extra
+
+
+ This controls the number of extra forms the formset will display in addition
+ to the initial forms. See the
+ :doc:`formsets documentation </topics/forms/formsets>` for more information.
+
+ .. versionadded:: 1.2
+
+ For users with JavaScript-enabled browsers, an "Add another" link is
+ provided to enable any number of additional inlines to be added in addition
+ to those provided as a result of the ``extra`` argument.
+
+ The dynamic link will not appear if the number of currently displayed forms
+ exceeds ``max_num``, or if the user does not have JavaScript enabled.
+
+.. _ref-contrib-admin-inline-max-num:
+
+.. attribute:: InlineModelAdmin.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.
+
+.. attribute:: InlineModelAdmin.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",)
+
+
+.. attribute:: InlineModelAdmin.template
+
+ The template used to render the inline on the page.
+
+.. attribute:: InlineModelAdmin.verbose_name
+
+ An override to the ``verbose_name`` found in the model's inner ``Meta``
+ class.
+
+.. attribute:: InlineModelAdmin.verbose_name_plural
+
+ An override to the ``verbose_name_plural`` found in the model's inner
+ ``Meta`` class.
+
+.. attribute:: InlineModelAdmin.can_delete
+
+ Specifies whether or not inline objects can be deleted in the inline.
+ Defaults to ``True``.
+
+
+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 Models
+--------------------------------
+
+.. versionadded:: 1.2
+
+By default, admin widgets for many-to-many relations will be displayed
+on whichever model contains the actual reference to the ``ManyToManyField``.
+Depending on your ``ModelAdmin`` definition, each many-to-many field in your
+model will be represented by a standard HTML ``<select multiple>``, a
+horizontal or vertical filter, or a ``raw_id_admin`` widget. However, it is
+also possible to to replace these widgets with inlines.
+
+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, related_name='groups')
+
+If you want to display many-to-many relations using an inline, you can do
+so by defining an ``InlineModelAdmin`` object for the relationship::
+
+ class MembershipInline(admin.TabularInline):
+ model = Group.members.through
+
+ class PersonAdmin(admin.ModelAdmin):
+ inlines = [
+ MembershipInline,
+ ]
+
+ class GroupAdmin(admin.ModelAdmin):
+ inlines = [
+ MembershipInline,
+ ]
+ exclude = ('members',)
+
+There are two features worth noting in this example.
+
+Firstly - the ``MembershipInline`` class references ``Group.members.through``.
+The ``through`` attribute is a reference to the model that manages the
+many-to-many relation. This model is automatically created by Django when you
+define a many-to-many field.
+
+Secondly, the ``GroupAdmin`` must manually exclude the ``members`` field.
+Django displays an admin widget for a many-to-many field on the model that
+defines the relation (in this case, ``Group``). If you want to use an inline
+model to represent the many-to-many relationship, you must tell Django's admin
+to *not* display this widget - otherwise you will end up with two widgets on
+your admin page for managing the relation.
+
+In all other respects, the ``InlineModelAdmin`` is exactly the same as any
+other. You can customize the appearance using any of the normal
+``ModelAdmin`` properties.
+
+Working with Many-to-Many Intermediary Models
+----------------------------------------------
+
+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
+:doc:`contenttypes documentation </ref/contrib/contenttypes>` for more specific
+information.
+
+Overriding Admin Templates
+==========================
+
+It is relatively easy to override many of the templates which the admin module
+uses to generate the various pages of an admin site. You can even override a few
+of these templates for a specific app, or a specific model.
+
+Set up your projects admin template directories
+-----------------------------------------------
+
+The admin template files are located in the ``contrib/admin/templates/admin``
+directory.
+
+In order to override one or more of them, first create an ``admin`` directory in
+your project's ``templates`` directory. This can be any of the directories you
+specified in ``TEMPLATE_DIRS``.
+
+Within this ``admin`` directory, create sub-directories named after your app.
+Within these app subdirectories create sub-directories named after your models.
+Note, that the admin app will lowercase the model name when looking for the
+directory, so make sure you name the directory in all lowercase if you are going
+to run your app on a case-sensitive filesystem.
+
+To override an admin template for a specific app, copy and edit the template
+from the ``django/contrib/admin/templates/admin`` directory, and save it to one
+of the directories you just created.
+
+For example, if we wanted to add a tool to the change list view for all the
+models in an app named ``my_app``, we would copy
+``contrib/admin/templates/admin/change_list.html`` to the
+``templates/admin/my_app/`` directory of our project, and make any necessary
+changes.
+
+If we wanted to add a tool to the change list view for only a specific model
+named 'Page', we would copy that same file to the
+``templates/admin/my_app/page`` directory of our project.
+
+Overriding vs. replacing an admin template
+------------------------------------------
+
+Because of the modular design of the admin templates, it is usually neither
+necessary nor advisable to replace an entire template. It is almost always
+better to override only the section of the template which you need to change.
+
+To continue the example above, we want to add a new link next to the ``History``
+tool for the ``Page`` model. After looking at ``change_form.html`` we determine
+that we only need to override the ``object-tools`` block. Therefore here is our
+new ``change_form.html`` :
+
+.. code-block:: html+django
+
+ {% extends "admin/change_form.html" %}
+ {% load i18n %}
+ {% block object-tools %}
+ {% if change %}{% if not is_popup %}
+ <ul class="object-tools">
+ <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
+ <li><a href="mylink/" class="historylink">My Link</a></li>
+ {% if has_absolute_url %}
+ <li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
+ {% trans "View on site" %}</a>
+ </li>
+ {% endif%}
+ </ul>
+ {% endif %}{% endif %}
+ {% endblock %}
+
+And that's it! If we placed this file in the ``templates/admin/my_app``
+directory, our link would appear on every model's change form.
+
+Templates which may be overridden per app or model
+--------------------------------------------------
+
+Not every template in ``contrib/admin/templates/admin`` may be overridden per
+app or per model. The following can:
+
+ * ``app_index.html``
+ * ``change_form.html``
+ * ``change_list.html``
+ * ``delete_confirmation.html``
+ * ``object_history.html``
+
+For those templates that cannot be overridden in this way, you may still
+override them for your entire project. Just place the new version in your
+``templates/admin`` directory. This is particularly useful to create custom 404
+and 500 pages.
+
+.. note::
+
+ Some of the admin templates, such as ``change_list_request.html`` are used
+ to render custom inclusion tags. These may be overridden, but in such cases
+ you are probably better off creating your own version of the tag in question
+ and giving it a different name. That way you can use it selectively.
+
+Root and login templates
+------------------------
+
+If you wish to change the index, login or logout templates, you are better off
+creating your own ``AdminSite`` instance (see below), and changing the
+:attr:`AdminSite.index_template` , :attr:`AdminSite.login_template` or
+:attr:`AdminSite.logout_template` properties.
+
+``AdminSite`` objects
+=====================
+
+.. class:: AdminSite(name=None)
+
+A Django administrative site is represented by an instance of
+``django.contrib.admin.sites.AdminSite``; by default, an instance of
+this class is created as ``django.contrib.admin.site`` and you can
+register your models and ``ModelAdmin`` instances with it.
+
+If you'd like to set up your own administrative site with custom
+behavior, however, you're free to subclass ``AdminSite`` and override
+or add anything you like. Then, simply create an instance of your
+``AdminSite`` subclass (the same way you'd instantiate any other
+Python class), and register your models and ``ModelAdmin`` subclasses
+with it instead of using the default.
+
+.. versionadded:: 1.1
+
+When constructing an instance of an ``AdminSite``, you are able to provide
+a unique instance name using the ``name`` argument to the constructor. This
+instance name is used to identify the instance, especially when
+:ref:`reversing admin URLs <admin-reverse-urls>`. If no instance name is
+provided, a default instance name of ``admin`` will be used.
+
+``AdminSite`` attributes
+------------------------
+
+Templates can override or extend base admin templates as described in
+`Overriding Admin Templates`_.
+
+.. attribute:: AdminSite.index_template
+
+Path to a custom template that will be used by the admin site main index view.
+
+.. attribute:: AdminSite.login_template
+
+Path to a custom template that will be used by the admin site login view.
+
+.. attribute:: AdminSite.logout_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site logout view.
+
+.. attribute:: AdminSite.password_change_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site password change
+view.
+
+.. attribute:: AdminSite.password_change_done_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site password change
+done view.
+
+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.urls`` 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('',
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+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('',
+ (r'^myadmin/', include(admin_site.urls)),
+ )
+
+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.
+
+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.
+
+.. versionchanged:: 1.1
+ The method for hooking ``AdminSite`` instances into urls has changed in
+ Django 1.1.
+
+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('',
+ (r'^basic-admin/', include(basic_site.urls)),
+ (r'^advanced-admin/', include(advanced_site.urls)),
+ )
+
+``AdminSite`` instances take a single argument to their constructor, their
+name, which can be anything you like. This argument becomes the prefix to the
+URL names for the purposes of :ref:`reversing them<admin-reverse-urls>`. This
+is only necessary if you are using more than one ``AdminSite``.
+
+Adding views to admin sites
+---------------------------
+
+.. versionadded:: 1.1
+
+Just like :class:`ModelAdmin`, :class:`AdminSite` provides a
+:meth:`~django.contrib.admin.ModelAdmin.get_urls()` method
+that can be overridden to define additional views for the site. To add
+a new view to your admin site, extend the base
+:meth:`~django.contrib.admin.ModelAdmin.get_urls()` method to include
+a pattern for your new view.
+
+.. note::
+ Any view you render that uses the admin templates, or extends the base
+ admin template, should provide the ``current_app`` argument to
+ ``RequestContext`` or ``Context`` when rendering the template. It should
+ be set to either ``self.name`` if your view is on an ``AdminSite`` or
+ ``self.admin_site.name`` if your view is on a ``ModelAdmin``.
+
+.. _admin-reverse-urls:
+
+Reversing Admin URLs
+====================
+
+.. versionadded:: 1.1
+
+When an :class:`AdminSite` is deployed, the views provided by that site are
+accessible using Django's :ref:`URL reversing system <naming-url-patterns>`.
+
+The :class:`AdminSite` provides the following named URL patterns:
+
+ ====================== ======================== =============
+ Page URL name Parameters
+ ====================== ======================== =============
+ Index ``index``
+ Logout ``logout``
+ Password change ``password_change``
+ Password change done ``password_change_done``
+ i18n javascript ``jsi18n``
+ Application index page ``app_list`` ``app_label``
+ ====================== ======================== =============
+
+Each :class:`ModelAdmin` instance provides an additional set of named URLs:
+
+ ====================== =============================================== =============
+ Page URL name Parameters
+ ====================== =============================================== =============
+ Changelist ``{{ app_label }}_{{ model_name }}_changelist``
+ Add ``{{ app_label }}_{{ model_name }}_add``
+ History ``{{ app_label }}_{{ model_name }}_history`` ``object_id``
+ Delete ``{{ app_label }}_{{ model_name }}_delete`` ``object_id``
+ Change ``{{ app_label }}_{{ model_name }}_change`` ``object_id``
+ ====================== =============================================== =============
+
+These named URLs are registered with the application namespace ``admin``, and
+with an instance namespace corresponding to the name of the Site instance.
+
+So - if you wanted to get a reference to the Change view for a particular
+``Choice`` object (from the polls application) in the default admin, you would
+call::
+
+ >>> from django.core import urlresolvers
+ >>> c = Choice.objects.get(...)
+ >>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,))
+
+This will find the first registered instance of the admin application (whatever the instance
+name), and resolve to the view for changing ``poll.Choice`` instances in that instance.
+
+If you want to find a URL in a specific admin instance, provide the name of that instance
+as a ``current_app`` hint to the reverse call. For example, if you specifically wanted
+the admin view from the admin instance named ``custom``, you would need to call::
+
+ >>> change_url = urlresolvers.reverse('custom:polls_choice_change', args=(c.id,))
+
+For more details, see the documentation on :ref:`reversing namespaced URLs
+<topics-http-reversing-url-namespaces>`.
diff --git a/parts/django/docs/ref/contrib/auth.txt b/parts/django/docs/ref/contrib/auth.txt
new file mode 100644
index 0000000..619b38e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/auth.txt
@@ -0,0 +1,4 @@
+``django.contrib.auth``
+=======================
+
+See :doc:`/topics/auth`.
diff --git a/parts/django/docs/ref/contrib/comments/custom.txt b/parts/django/docs/ref/contrib/comments/custom.txt
new file mode 100644
index 0000000..5411d9c
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/custom.txt
@@ -0,0 +1,202 @@
+==================================
+Customizing the comments framework
+==================================
+
+.. currentmodule:: django.contrib.comments
+
+If the built-in comment framework doesn't quite fit your needs, you can extend
+the comment app's behavior to add custom data and logic. The comments framework
+lets you extend the built-in comment model, the built-in comment form, and the
+various comment views.
+
+The :setting:`COMMENTS_APP` setting is where this customization begins. Set
+:setting:`COMMENTS_APP` to the name of the app you'd like to use to provide
+custom behavior. You'll use the same syntax as you'd use for
+:setting:`INSTALLED_APPS`, and the app given must also be in the
+:setting:`INSTALLED_APPS` list.
+
+For example, if you wanted to use an app named ``my_comment_app``, your
+settings file would contain::
+
+ INSTALLED_APPS = [
+ ...
+ 'my_comment_app',
+ ...
+ ]
+
+ COMMENTS_APP = 'my_comment_app'
+
+The app named in :setting:`COMMENTS_APP` provides its custom behavior by
+defining some module-level functions in the app's ``__init__.py``. The
+:ref:`complete list of these functions <custom-comment-app-api>` can be found
+below, but first let's look at a quick example.
+
+An example custom comments app
+==============================
+
+One of the most common types of customization is modifying the set of fields
+provided on the built-in comment model. For example, some sites that allow
+comments want the commentator to provide a title for their comment; the built-in
+comment model has no field for that title.
+
+To make this kind of customization, we'll need to do three things:
+
+ #. Create a custom comment :class:`~django.db.models.Model` that adds on the
+ "title" field.
+
+ #. Create a custom comment :class:`~django.forms.Form` that also adds this
+ "title" field.
+
+ #. Inform Django of these objects by defining a few functions in a
+ custom :setting:`COMMENTS_APP`.
+
+So, carrying on the example above, we're dealing with a typical app structure in
+the ``my_custom_app`` directory::
+
+ my_custom_app/
+ __init__.py
+ models.py
+ forms.py
+
+In the ``models.py`` we'll define a ``CommentWithTitle`` model::
+
+ from django.db import models
+ from django.contrib.comments.models import Comment
+
+ class CommentWithTitle(Comment):
+ title = models.CharField(max_length=300)
+
+Most custom comment models will subclass the :class:`Comment` model. However,
+if you want to substantially remove or change the fields available in the
+:class:`Comment` model, but don't want to rewrite the templates, you could
+try subclassing from :class:`BaseCommentAbstractModel`.
+
+Next, we'll define a custom comment form in ``forms.py``. This is a little more
+tricky: we have to both create a form and override
+:meth:`CommentForm.get_comment_model` and
+:meth:`CommentForm.get_comment_create_data` to return deal with our custom title
+field::
+
+ from django import forms
+ from django.contrib.comments.forms import CommentForm
+ from my_comment_app.models import CommentWithTitle
+
+ class CommentFormWithTitle(CommentForm):
+ title = forms.CharField(max_length=300)
+
+ def get_comment_model(self):
+ # Use our custom comment model instead of the built-in one.
+ return CommentWithTitle
+
+ def get_comment_create_data(self):
+ # Use the data of the superclass, and add in the title field
+ data = super(CommentFormWithTitle, self).get_comment_create_data()
+ data['title'] = self.cleaned_data['title']
+ return data
+
+Django provides a couple of "helper" classes to make writing certain types of
+custom comment forms easier; see :mod:`django.contrib.comments.forms` for
+more.
+
+Finally, we'll define a couple of methods in ``my_custom_app/__init__.py`` to
+point Django at these classes we've created::
+
+ from my_comments_app.models import CommentWithTitle
+ from my_comments_app.forms import CommentFormWithTitle
+
+ def get_model():
+ return CommentWithTitle
+
+ def get_form():
+ return CommentFormWithTitle
+
+
+.. warning::
+
+ Be careful not to create cyclic imports in your custom comments app.
+ If you feel your comment configuration isn't being used as defined --
+ for example, if your comment moderation policy isn't being applied --
+ you may have a cyclic import problem.
+
+ If you are having unexplained problems with comments behavior, check
+ if your custom comments application imports (even indirectly)
+ any module that itself imports Django's comments module.
+
+The above process should take care of most common situations. For more
+advanced usage, there are additional methods you can define. Those are
+explained in the next section.
+
+.. _custom-comment-app-api:
+
+Custom comment app API
+======================
+
+The :mod:`django.contrib.comments` app defines the following methods; any
+custom comment app must define at least one of them. All are optional,
+however.
+
+.. function:: get_model()
+
+ Return the :class:`~django.db.models.Model` class to use for comments. This
+ model should inherit from
+ :class:`django.contrib.comments.models.BaseCommentAbstractModel`, which
+ defines necessary core fields.
+
+ The default implementation returns
+ :class:`django.contrib.comments.models.Comment`.
+
+.. function:: get_form()
+
+ Return the :class:`~django.forms.Form` class you want to use for
+ creating, validating, and saving your comment model. Your custom
+ comment form should accept an additional first argument,
+ ``target_object``, which is the object the comment will be
+ attached to.
+
+ The default implementation returns
+ :class:`django.contrib.comments.forms.CommentForm`.
+
+ .. note::
+
+ The default comment form also includes a number of unobtrusive
+ spam-prevention features (see
+ :ref:`notes-on-the-comment-form`). If replacing it with your
+ own form, you may want to look at the source code for the
+ built-in form and consider incorporating similar features.
+
+.. function:: get_form_target()
+
+ Return the URL for POSTing comments. This will be the ``<form action>``
+ attribute when rendering your comment form.
+
+ The default implementation returns a reverse-resolved URL pointing
+ to the :func:`post_comment` view.
+
+ .. note::
+
+ If you provide a custom comment model and/or form, but you
+ want to use the default :func:`post_comment` view, you will
+ need to be aware that it requires the model and form to have
+ certain additional attributes and methods: see the
+ :func:`post_comment` view documentation for details.
+
+.. function:: get_flag_url()
+
+ Return the URL for the "flag this comment" view.
+
+ The default implementation returns a reverse-resolved URL pointing
+ to the :func:`django.contrib.comments.views.moderation.flag` view.
+
+.. function:: get_delete_url()
+
+ Return the URL for the "delete this comment" view.
+
+ The default implementation returns a reverse-resolved URL pointing
+ to the :func:`django.contrib.comments.views.moderation.delete` view.
+
+.. function:: get_approve_url()
+
+ Return the URL for the "approve this comment from moderation" view.
+
+ The default implementation returns a reverse-resolved URL pointing
+ to the :func:`django.contrib.comments.views.moderation.approve` view.
diff --git a/parts/django/docs/ref/contrib/comments/example.txt b/parts/django/docs/ref/contrib/comments/example.txt
new file mode 100644
index 0000000..424bdb1
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/example.txt
@@ -0,0 +1,208 @@
+.. highlightlang:: html+django
+
+===========================================
+Example of using the in-built comments app
+===========================================
+
+Follow the first three steps of the quick start guide in the
+:doc:`documentation </ref/contrib/comments/index>`.
+
+Now suppose, you have an app (``blog``) with a model (``Post``)
+to which you want to attach comments. Let us also suppose that
+you have a template called ``blog_detail.html`` where you want
+to display the comments list and comment form.
+
+Template
+========
+
+First, we should load the ``comment`` template tags in the
+``blog_detail.html`` so that we can use it's functionality. So
+just like all other custom template tag libraries::
+
+ {% load comments %}
+
+Next, let us add the number of comments attached to the particular
+model instance of ``Post``. For this we assume that a context
+variable ``object_pk`` is present which gives the ``id`` of the
+instance of ``Post``.
+
+The usage of the :ttag:`get_comment_count` tag is like below::
+
+ {% get_comment_count for blog.post object_pk as comment_count %}
+ <p>{{ comment_count }} comments have been posted.</p>
+
+If you have the instance (say ``entry``) of the model (``Post``)
+available in the context, then you can refer to it directly::
+
+ {% get_comment_count for entry as comment_count %}
+ <p>{{ comment_count }} comments have been posted.</p>
+
+.. versionadded:: 1.2
+
+Next, we can use the :ttag:`render_comment_list` tag, to render all comments
+to the given instance (``entry``) by using the ``comments/list.html`` template.
+
+ {% render_comment_list for entry %}
+
+Django will will look for the ``list.html`` under the following directories
+(for our example)::
+
+ comments/blog/post/list.html
+ comments/blog/list.html
+ comments/list.html
+
+To get a list of comments, we make use of the :ttag:`get_comment_list` tag.
+This tag's usage is very similar to the :ttag:`get_comment_count` tag. We
+need to remember that the :ttag:`get_comment_list` returns a list of comments
+and hence we will have to iterate through them to display them::
+
+ {% get_comment_list for blog.post object_pk as comment_list %}
+ {% for comment in comment_list %}
+ <p>Posted by: {{ comment.user_name }} on {{ comment.submit_date }}</p>
+ ...
+ <p>Comment: {{ comment.comment }}</p>
+ ...
+ {% endfor %}
+
+Finally, we display the comment form, enabling users to enter their
+comments. There are two ways of doing so. The first is when you want to
+display the comments template available under your ``comments/form.html``.
+The other method gives you a chance to customize the form.
+
+The first method makes use of the :ttag:`render_comment_form` tag. It's usage
+too is similar to the other three tags we have discussed above::
+
+ {% render_comment_form for entry %}
+
+It looks for the ``form.html`` under the following directories
+(for our example)::
+
+ comments/blog/post/form.html
+ comments/blog/form.html
+ comments/form.html
+
+Since we customize the form in the second method, we make use of another
+tag called :ttag:`comment_form_target`. This tag on rendering gives the URL
+where the comment form is posted. Without any :doc:`customization
+</ref/contrib/comments/custom>`, :ttag:`comment_form_target` evaluates to
+``/comments/post/``. We use this tag in the form's ``action`` attribute.
+
+The :ttag:`get_comment_form` tag renders a ``form`` for a model instance by
+creating a context variable. One can iterate over the ``form`` object to
+get individual fields. This gives you fine-grain control over the form::
+
+ {% for field in form %}
+ {% ifequal field.name "comment" %}
+ <!-- Customize the "comment" field, say, make CSS changes -->
+ ...
+ {% endfor %}
+
+But let's look at a simple example::
+
+ {% get_comment_form for entry as form %}
+ <!-- A context variable called form is created with the necessary hidden
+ fields, timestamps and security hashes -->
+ <table>
+ <form action="{% comment_form_target %}" method="post">
+ {{ form }}
+ <tr>
+ <td></td>
+ <td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
+ </tr>
+ </form>
+ </table>
+
+Flagging
+========
+
+If you want your users to be able to flag comments (say for profanity), you
+can just direct them (by placing a link in your comment list) to ``/flag/{{
+comment.id }}/``. Similarly, a user with requisite permissions (``"Can
+moderate comments"``) can approve and delete comments. This can also be
+done through the ``admin`` as you'll see later. You might also want to
+customize the following templates:
+
+ * ``flag.html``
+ * ``flagged.html``
+ * ``approve.html``
+ * ``approved.html``
+ * ``delete.html``
+ * ``deleted.html``
+
+found under the directory structure we saw for ``form.html``.
+
+Feeds
+=====
+
+Suppose you want to export a :doc:`feed </ref/contrib/syndication>` of the
+latest comments, you can use the in-built :class:`LatestCommentFeed`. Just
+enable it in your project's ``urls.py``:
+
+.. code-block:: python
+
+ from django.conf.urls.defaults import *
+ from django.contrib.comments.feeds import LatestCommentFeed
+
+ feeds = {
+ 'latest': LatestCommentFeed,
+ }
+
+ urlpatterns = patterns('',
+ # ...
+ (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
+ {'feed_dict': feeds}),
+ # ...
+ )
+
+Now you should have the latest comment feeds being served off ``/feeds/latest/``.
+
+Moderation
+==========
+
+Now that we have the comments framework working, we might want to have some
+moderation setup to administer the comments. The comments framework comes
+in-built with :doc:`generic comment moderation
+</ref/contrib/comments/moderation>`. The comment moderation has the following
+features (all of which or only certain can be enabled):
+
+ * Enable comments for a particular model instance.
+ * Close comments after a particular (user-defined) number of days.
+ * Email new comments to the site-staff.
+
+To enable comment moderation, we subclass the :class:`CommentModerator` and
+register it with the moderation features we want. Let us suppose we want to
+close comments after 7 days of posting and also send out an email to the
+site staff. In ``blog/models.py``, we register a comment moderator in the
+following way:
+
+.. code-block:: python
+
+ from django.contrib.comments.moderation import CommentModerator, moderator
+ from django.db import models
+
+ class Post(models.Model):
+ title = models.CharField(max_length = 255)
+ content = models.TextField()
+ posted_date = models.DateTimeField()
+
+ class PostModerator(CommentModerator):
+ email_notification = True
+ auto_close_field = 'posted_date'
+ # Close the comments after 7 days.
+ close_after = 7
+
+ moderator.register(Post, PostModerator)
+
+The generic comment moderation also has the facility to remove comments.
+These comments can then be moderated by any user who has access to the
+``admin`` site and the ``Can moderate comments`` permission (can be set
+under the ``Users`` page in the ``admin``).
+
+The moderator can ``Flag``, ``Approve`` or ``Remove`` comments using the
+``Action`` drop-down in the ``admin`` under the ``Comments`` page.
+
+.. note::
+
+ Only a super-user will be able to delete comments from the database.
+ ``Remove Comments`` only sets the ``is_public`` attribute to
+ ``False``.
diff --git a/parts/django/docs/ref/contrib/comments/forms.txt b/parts/django/docs/ref/contrib/comments/forms.txt
new file mode 100644
index 0000000..c21a27b
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/forms.txt
@@ -0,0 +1,46 @@
+====================
+Comment form classes
+====================
+
+.. module:: django.contrib.comments.forms
+ :synopsis: Forms for dealing with the built-in comment model.
+
+The ``django.contrib.comments.forms`` module contains a handful of forms
+you'll use when writing custom views dealing with comments, or when writing
+:doc:`custom comment apps </ref/contrib/comments/custom>`.
+
+.. class:: CommentForm
+
+ The main comment form representing the standard, built-in way of handling
+ submitted comments. This is the class used by all the views
+ :mod:`django.contrib.comments` to handle submitted comments.
+
+ If you want to build custom views that are similar to Django's built-in
+ comment handling views, you'll probably want to use this form.
+
+Abstract comment forms for custom comment apps
+----------------------------------------------
+
+If you're building a :doc:`custom comment app </ref/contrib/comments/custom>`,
+you might want to replace *some* of the form logic but still rely on parts of
+the existing form.
+
+:class:`CommentForm` is actually composed of a couple of abstract base class
+forms that you can subclass to reuse pieces of the form handling logic:
+
+.. class:: CommentSecurityForm
+
+ Handles the anti-spoofing protection aspects of the comment form handling.
+
+ This class contains the ``content_type`` and ``object_pk`` fields pointing
+ to the object the comment is attached to, along with a ``timestamp`` and a
+ ``security_hash`` of all the form data. Together, the timestamp and the
+ security hash ensure that spammers can't "replay" form submissions and
+ flood you with comments.
+
+.. class:: CommentDetailsForm
+
+ Handles the details of the comment itself.
+
+ This class contains the ``name``, ``email``, ``url``, and the ``comment``
+ field itself, along with the associated validation logic. \ No newline at end of file
diff --git a/parts/django/docs/ref/contrib/comments/index.txt b/parts/django/docs/ref/contrib/comments/index.txt
new file mode 100644
index 0000000..817871e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/index.txt
@@ -0,0 +1,302 @@
+===========================
+Django's comments framework
+===========================
+
+.. module:: django.contrib.comments
+ :synopsis: Django's comment framework
+
+.. highlightlang:: html+django
+
+Django includes a simple, yet customizable comments framework. The built-in
+comments framework can be used to attach comments to any model, so you can use
+it for comments on blog entries, photos, book chapters, or anything else.
+
+.. note::
+
+ If you used to use Django's older (undocumented) comments framework, you'll
+ need to upgrade. See the :doc:`upgrade guide </ref/contrib/comments/upgrade>`
+ for instructions.
+
+Quick start guide
+=================
+
+To get started using the ``comments`` app, follow these steps:
+
+ #. Install the comments framework by adding ``'django.contrib.comments'`` to
+ :setting:`INSTALLED_APPS`.
+
+ #. Run ``manage.py syncdb`` so that Django will create the comment tables.
+
+ #. Add the comment app's URLs to your project's ``urls.py``:
+
+ .. code-block:: python
+
+ urlpatterns = patterns('',
+ ...
+ (r'^comments/', include('django.contrib.comments.urls')),
+ ...
+ )
+
+ #. Use the `comment template tags`_ below to embed comments in your
+ templates.
+
+You might also want to examine :doc:`/ref/contrib/comments/settings`.
+
+Comment template tags
+=====================
+
+You'll primarily interact with the comment system through a series of template
+tags that let you embed comments and generate forms for your users to post them.
+
+Like all custom template tag libraries, you'll need to :ref:`load the custom
+tags <loading-custom-template-libraries>` before you can use them::
+
+ {% load comments %}
+
+Once loaded you can use the template tags below.
+
+Specifying which object comments are attached to
+------------------------------------------------
+
+Django's comments are all "attached" to some parent object. This can be any
+instance of a Django model. Each of the tags below gives you a couple of
+different ways you can specify which object to attach to:
+
+ #. Refer to the object directly -- the more common method. Most of the
+ time, you'll have some object in the template's context you want
+ to attach the comment to; you can simply use that object.
+
+ For example, in a blog entry page that has a variable named ``entry``,
+ you could use the following to load the number of comments::
+
+ {% get_comment_count for entry as comment_count %}.
+
+ #. Refer to the object by content-type and object id. You'd use this method
+ if you, for some reason, don't actually have direct access to the object.
+
+ Following the above example, if you knew the object ID was ``14`` but
+ didn't have access to the actual object, you could do something like::
+
+ {% get_comment_count for blog.entry 14 as comment_count %}
+
+ In the above, ``blog.entry`` is the app label and (lower-cased) model
+ name of the model class.
+
+Displaying comments
+-------------------
+
+To display a list of comments, you can use the template tags
+:ttag:`render_comment_list` or :ttag:`get_comment_list`.
+
+.. templatetag:: render_comment_list
+
+Quickly rendering a comment list
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The easiest way to display a list of comments for some object is by using
+:ttag:`render_comment_list`::
+
+ {% render_comment_list for [object] %}
+
+For example::
+
+ {% render_comment_list for event %}
+
+This will render comments using a template named ``comments/list.html``, a
+default version of which is included with Django.
+
+.. templatetag:: get_comment_list
+
+Rendering a custom comment list
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the list of comments for some object, use :ttag:`get_comment_list`::
+
+ {% get_comment_list for [object] as [varname] %}
+
+For example::
+
+ {% get_comment_list for event as comment_list %}
+ {% for comment in comment_list %}
+ ...
+ {% endfor %}
+
+This returns a list of :class:`~django.contrib.comments.models.Comment` objects;
+see :doc:`the comment model documentation </ref/contrib/comments/models>` for
+details.
+
+.. templatetag:: get_comment_permalink
+
+Linking to comments
+-------------------
+
+.. versionadded:: 1.2
+
+To provide a permalink to a specific comment, use :ttag:`get_comment_permalink`::
+
+ {% get_comment_permalink comment_obj [format_string] %}
+
+By default, the named anchor that will be appended to the URL will be the letter
+'c' followed by the comment id, for example 'c82'. You may specify a custom
+format string if you wish to override this behavior::
+
+ {% get_comment_permalink comment "#c%(id)s-by-%(user_name)s"%}
+
+The format string is a standard python format string. Valid mapping keys
+include any attributes of the comment object.
+
+Regardless of whether you specify a custom anchor pattern, you must supply a
+matching named anchor at a suitable place in your template.
+
+For example::
+
+ {% for comment in comment_list %}
+ <a name="c{{ comment.id }}"></a>
+ <a href="{% get_comment_permalink comment %}">
+ permalink for comment #{{ forloop.counter }}
+ </a>
+ ...
+ {% endfor %}
+
+.. warning::
+
+ There's a known bug in Safari/Webkit which causes the named anchor to be
+ forgotten following a redirect. The practical impact for comments is that
+ the Safari/webkit browsers will arrive at the correct page but will not
+ scroll to the named anchor.
+
+.. templatetag:: get_comment_count
+
+Counting comments
+-----------------
+
+To count comments attached to an object, use :ttag:`get_comment_count`::
+
+ {% get_comment_count for [object] as [varname] %}
+
+For example::
+
+ {% get_comment_count for event as comment_count %}
+
+ <p>This event has {{ comment_count }} comments.</p>
+
+
+Displaying the comment post form
+--------------------------------
+
+To show the form that users will use to post a comment, you can use
+:ttag:`render_comment_form` or :ttag:`get_comment_form`
+
+.. templatetag:: render_comment_form
+
+Quickly rendering the comment form
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The easiest way to display a comment form is by using
+:ttag:`render_comment_form`::
+
+ {% render_comment_form for [object] %}
+
+For example::
+
+ {% render_comment_form for event %}
+
+This will render comments using a template named ``comments/form.html``, a
+default version of which is included with Django.
+
+.. templatetag:: get_comment_form
+
+Rendering a custom comment form
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want more control over the look and feel of the comment form, you use use
+:ttag:`get_comment_form` to get a :doc:`form object </topics/forms/index>` that
+you can use in the template::
+
+ {% get_comment_form for [object] as [varname] %}
+
+A complete form might look like::
+
+ {% get_comment_form for event as form %}
+ <form action="{% comment_form_target %}" method="post">
+ {{ form }}
+ <tr>
+ <td></td>
+ <td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
+ </tr>
+ </form>
+
+Be sure to read the `notes on the comment form`_, below, for some special
+considerations you'll need to make if you're using this approach.
+
+.. templatetag:: comment_form_target
+
+Getting the comment form target
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You may have noticed that the above example uses another template tag --
+:ttag:`comment_form_target` -- to actually get the ``action`` attribute of the
+form. This will always return the correct URL that comments should be posted to;
+you'll always want to use it like above::
+
+ <form action="{% comment_form_target %}" method="post">
+
+Redirecting after the comment post
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To specify the URL you want to redirect to after the comment has been posted,
+you can include a hidden form input called ``next`` in your comment form. For example::
+
+ <input type="hidden" name="next" value="{% url my_comment_was_posted %}" />
+
+.. _notes-on-the-comment-form:
+
+Notes on the comment form
+-------------------------
+
+The form used by the comment system has a few important anti-spam attributes you
+should know about:
+
+ * It contains a number of hidden fields that contain timestamps, information
+ about the object the comment should be attached to, and a "security hash"
+ used to validate this information. If someone tampers with this data --
+ something comment spammers will try -- the comment submission will fail.
+
+ If you're rendering a custom comment form, you'll need to make sure to
+ pass these values through unchanged.
+
+ * The timestamp is used to ensure that "reply attacks" can't continue very
+ long. Users who wait too long between requesting the form and posting a
+ comment will have their submissions refused.
+
+ * The comment form includes a "honeypot_" field. It's a trap: if any data is
+ entered in that field, the comment will be considered spam (spammers often
+ automatically fill in all fields in an attempt to make valid submissions).
+
+ The default form hides this field with a piece of CSS and further labels
+ it with a warning field; if you use the comment form with a custom
+ template you should be sure to do the same.
+
+The comments app also depends on the more general :doc:`Cross Site Request
+Forgery protection </ref/contrib/csrf>` that comes with Django. As described in
+the documentation, it is best to use ``CsrfViewMiddleware``. However, if you
+are not using that, you will need to use the ``csrf_protect`` decorator on any
+views that include the comment form, in order for those views to be able to
+output the CSRF token and cookie.
+
+.. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing)
+
+More information
+================
+
+.. toctree::
+ :maxdepth: 1
+
+ models
+ settings
+ signals
+ upgrade
+ custom
+ forms
+ moderation
+ example
diff --git a/parts/django/docs/ref/contrib/comments/models.txt b/parts/django/docs/ref/contrib/comments/models.txt
new file mode 100644
index 0000000..e773790
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/models.txt
@@ -0,0 +1,80 @@
+===========================
+The built-in comment models
+===========================
+
+.. module:: django.contrib.comments.models
+ :synopsis: The built-in comment models
+
+.. class:: Comment
+
+ Django's built-in comment model. Has the following fields:
+
+ .. attribute:: content_object
+
+ A :class:`~django.contrib.contettypes.generic.GenericForeignKey`
+ attribute pointing to the object the comment is attached to. You can use
+ this to get at the related object (i.e. ``my_comment.content_object``).
+
+ Since this field is a
+ :class:`~django.contrib.contettypes.generic.GenericForeignKey`, it's
+ actually syntactic sugar on top of two underlying attributes, described
+ below.
+
+ .. attribute:: content_type
+
+ A :class:`~django.db.models.ForeignKey` to
+ :class:`~django.contrib.contenttypes.models.ContentType`; this is the
+ type of the object the comment is attached to.
+
+ .. attribute:: object_pk
+
+ A :class:`~django.db.models.TextField` containing the primary
+ key of the object the comment is attached to.
+
+ .. attribute:: site
+
+ A :class:`~django.db.models.ForeignKey` to the
+ :class:`~django.contrib.sites.models.Site` on which the comment was
+ posted.
+
+ .. attribute:: user
+
+ A :class:`~django.db.models.ForeignKey` to the
+ :class:`~django.contrib.auth.models.User` who posted the comment.
+ May be blank if the comment was posted by an unauthenticated user.
+
+ .. attribute:: user_name
+
+ The name of the user who posted the comment.
+
+ .. attribute:: user_email
+
+ The email of the user who posted the comment.
+
+ .. attribute:: user_url
+
+ The URL entered by the person who posted the comment.
+
+ .. attribute:: comment
+
+ The actual content of the comment itself.
+
+ .. attribute:: submit_date
+
+ The date the comment was submitted.
+
+ .. attribute:: ip_address
+
+ The IP address of the user posting the comment.
+
+ .. attribute:: is_public
+
+ ``False`` if the comment is in moderation (see
+ :doc:`/ref/contrib/comments/moderation`); If ``True``, the comment will
+ be displayed on the site.
+
+ .. attribute:: is_removed
+
+ ``True`` if the comment was removed. Used to keep track of removed
+ comments instead of just deleting them.
+
diff --git a/parts/django/docs/ref/contrib/comments/moderation.txt b/parts/django/docs/ref/contrib/comments/moderation.txt
new file mode 100644
index 0000000..519bc5e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/moderation.txt
@@ -0,0 +1,230 @@
+==========================
+Generic comment moderation
+==========================
+
+.. module:: django.contrib.comments.moderation
+ :synopsis: Support for automatic comment moderation.
+
+Django's bundled comments application is extremely useful on its own,
+but the amount of comment spam circulating on the Web today
+essentially makes it necessary to have some sort of automatic
+moderation system in place for any application which makes use of
+comments. To make this easier to handle in a consistent fashion,
+``django.contrib.comments.moderation`` provides a generic, extensible
+comment-moderation system which can be applied to any model or set of
+models which want to make use of Django's comment system.
+
+
+Overview
+========
+
+The entire system is contained within ``django.contrib.comments.moderation``,
+and uses a two-step process to enable moderation for any given model:
+
+1. A subclass of :class:`CommentModerator`
+ is defined which specifies the moderation options the model wants to
+ enable.
+
+2. The model is registered with the moderation system, passing in the
+ model class and the class which specifies its moderation options.
+
+A simple example is the best illustration of this. Suppose we have the
+following model, which would represent entries in a Weblog::
+
+ from django.db import models
+
+ class Entry(models.Model):
+ title = models.CharField(maxlength=250)
+ body = models.TextField()
+ pub_date = models.DateTimeField()
+ enable_comments = models.BooleanField()
+
+Now, suppose that we want the following steps to be applied whenever a
+new comment is posted on an ``Entry``:
+
+1. If the ``Entry``'s ``enable_comments`` field is ``False``, the
+ comment will simply be disallowed (i.e., immediately deleted).
+
+2. If the ``enable_comments`` field is ``True``, the comment will be
+ allowed to save.
+
+3. Once the comment is saved, an email should be sent to site staff
+ notifying them of the new comment.
+
+Accomplishing this is fairly straightforward and requires very little
+code::
+
+ from django.contrib.comments.moderation import CommentModerator, moderator
+
+ class EntryModerator(CommentModerator):
+ email_notification = True
+ enable_field = 'enable_comments'
+
+ moderator.register(Entry, EntryModerator)
+
+The :class:`CommentModerator` class pre-defines a number of useful moderation
+options which subclasses can enable or disable as desired, and ``moderator``
+knows how to work with them to determine whether to allow a comment, whether
+to moderate a comment which will be allowed to post, and whether to email
+notifications of new comments.
+
+Built-in moderation options
+---------------------------
+
+.. class:: CommentModerator
+
+ Most common comment-moderation needs can be handled by subclassing
+ :class:`CommentModerator` and
+ changing the values of pre-defined attributes; the full range of built-in
+ options is as follows.
+
+ .. attribute:: auto_close_field
+
+ If this is set to the name of a
+ :class:`~django.db.models.fields.DateField` or
+ :class:`~django.db.models.fields.DateTimeField` on the model for which
+ comments are being moderated, new comments for objects of that model
+ will be disallowed (immediately deleted) when a certain number of days
+ have passed after the date specified in that field. Must be
+ used in conjunction with :attr:`close_after`, which specifies the
+ number of days past which comments should be
+ disallowed. Default value is ``None``.
+
+ .. attribute:: auto_moderate_field
+
+ Like :attr:`auto_close_field`, but instead of outright deleting
+ new comments when the requisite number of days have elapsed,
+ it will simply set the ``is_public`` field of new comments to
+ ``False`` before saving them. Must be used in conjunction with
+ :attr:`moderate_after`, which specifies the number of days past
+ which comments should be moderated. Default value is ``None``.
+
+ .. attribute:: close_after
+
+ If :attr:`auto_close_field` is used, this must specify the number
+ of days past the value of the field specified by
+ :attr:`auto_close_field` after which new comments for an object
+ should be disallowed. Default value is ``None``.
+
+ .. attribute:: email_notification
+
+ If ``True``, any new comment on an object of this model which
+ survives moderation (i.e., is not deleted) will generate an
+ email to site staff. Default value is ``False``.
+
+ .. attribute:: enable_field
+
+ If this is set to the name of a
+ :class:`~django.db.models.fields.BooleanField` on the model
+ for which comments are being moderated, new comments on
+ objects of that model will be disallowed (immediately deleted)
+ whenever the value of that field is ``False`` on the object
+ the comment would be attached to. Default value is ``None``.
+
+ .. attribute:: moderate_after
+
+ If :attr:`auto_moderate_field` is used, this must specify the number
+ of days past the value of the field specified by
+ :attr:`auto_moderate_field` after which new comments for an object
+ should be marked non-public. Default value is ``None``.
+
+Simply subclassing :class:`CommentModerator` and changing the values of these
+options will automatically enable the various moderation methods for any
+models registered using the subclass.
+
+Adding custom moderation methods
+--------------------------------
+
+For situations where the built-in options listed above are not
+sufficient, subclasses of :class:`CommentModerator` can also override
+the methods which actually perform the moderation, and apply any logic
+they desire. :class:`CommentModerator` defines three methods which
+determine how moderation will take place; each method will be called
+by the moderation system and passed two arguments: ``comment``, which
+is the new comment being posted, ``content_object``, which is the
+object the comment will be attached to, and ``request``, which is the
+:class:`~django.http.HttpRequest` in which the comment is being submitted:
+
+.. method:: CommentModerator.allow(comment, content_object, request)
+
+ Should return ``True`` if the comment should be allowed to
+ post on the content object, and ``False`` otherwise (in which
+ case the comment will be immediately deleted).
+
+.. method:: CommentModerator.email(comment, content_object, request)
+
+ If email notification of the new comment should be sent to
+ site staff or moderators, this method is responsible for
+ sending the email.
+
+.. method:: CommentModerator.moderate(comment, content_object, request)
+
+ Should return ``True`` if the comment should be moderated (in
+ which case its ``is_public`` field will be set to ``False``
+ before saving), and ``False`` otherwise (in which case the
+ ``is_public`` field will not be changed).
+
+
+Registering models for moderation
+---------------------------------
+
+The moderation system, represented by
+``django.contrib.comments.moderation.moderator`` is an instance of the class
+:class:`Moderator`, which allows registration and "unregistration" of models
+via two methods:
+
+.. function:: moderator.register(model_or_iterable, moderation_class)
+
+ Takes two arguments: the first should be either a model class
+ or list of model classes, and the second should be a subclass
+ of ``CommentModerator``, and register the model or models to
+ be moderated using the options defined in the
+ ``CommentModerator`` subclass. If any of the models are
+ already registered for moderation, the exception
+ :exc:`AlreadyModerated` will be raised.
+
+.. function:: moderator.unregister(model_or_iterable)
+
+ Takes one argument: a model class or list of model classes,
+ and removes the model or models from the set of models which
+ are being moderated. If any of the models are not currently
+ being moderated, the exception
+ :exc:`NotModerated` will be raised.
+
+
+Customizing the moderation system
+---------------------------------
+
+Most use cases will work easily with simple subclassing of
+:class:`CommentModerator` and registration with the provided
+:class:`Moderator` instance, but customization of global moderation behavior
+can be achieved by subclassing :class:`Moderator` and instead registering
+models with an instance of the subclass.
+
+.. class:: Moderator
+
+ In addition to the :meth:`Moderator.register` and
+ :meth:`Moderator.unregister` methods detailed above, the following methods
+ on :class:`Moderator` can be overridden to achieve customized behavior:
+
+ .. method:: connect
+
+ Determines how moderation is set up globally. The base
+ implementation in
+ :class:`Moderator` does this by
+ attaching listeners to the :data:`~django.contrib.comments.signals.comment_will_be_posted`
+ and :data:`~django.contrib.comments.signals.comment_was_posted` signals from the
+ comment models.
+
+ .. method:: pre_save_moderation(sender, comment, request, **kwargs)
+
+ In the base implementation, applies all pre-save moderation
+ steps (such as determining whether the comment needs to be
+ deleted, or whether it needs to be marked as non-public or
+ generate an email).
+
+ .. method:: post_save_moderation(sender, comment, request, **kwargs)
+
+ In the base implementation, applies all post-save moderation
+ steps (currently this consists entirely of deleting comments
+ which were disallowed).
diff --git a/parts/django/docs/ref/contrib/comments/settings.txt b/parts/django/docs/ref/contrib/comments/settings.txt
new file mode 100644
index 0000000..1f1aeca
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/settings.txt
@@ -0,0 +1,33 @@
+================
+Comment settings
+================
+
+These settings configure the behavior of the comments framework:
+
+.. setting:: COMMENTS_HIDE_REMOVED
+
+COMMENTS_HIDE_REMOVED
+---------------------
+
+If ``True`` (default), removed comments will be excluded from comment
+lists/counts (as taken from template tags). Otherwise, the template author is
+responsible for some sort of a "this comment has been removed by the site staff"
+message.
+
+.. setting:: COMMENT_MAX_LENGTH
+
+COMMENT_MAX_LENGTH
+------------------
+
+The maximum length of the comment field, in characters. Comments longer than
+this will be rejected. Defaults to 3000.
+
+.. setting:: COMMENTS_APP
+
+COMMENTS_APP
+------------
+
+An app which provides :doc:`customization of the comments framework
+</ref/contrib/comments/custom>`. Use the same dotted-string notation
+as in :setting:`INSTALLED_APPS`. Your custom :setting:`COMMENTS_APP`
+must also be listed in :setting:`INSTALLED_APPS`.
diff --git a/parts/django/docs/ref/contrib/comments/signals.txt b/parts/django/docs/ref/contrib/comments/signals.txt
new file mode 100644
index 0000000..7ae34a1
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/signals.txt
@@ -0,0 +1,91 @@
+================================
+Signals sent by the comments app
+================================
+
+.. module:: django.contrib.comments.signals
+ :synopsis: Signals sent by the comment module.
+
+The comment app sends a series of :doc:`signals </topics/signals>` to allow for
+comment moderation and similar activities. See :doc:`the introduction to signals
+</topics/signals>` for information about how to register for and receive these
+signals.
+
+comment_will_be_posted
+======================
+
+.. data:: django.contrib.comments.signals.comment_will_be_posted
+ :module:
+
+Sent just before a comment will be saved, after it's been sanity checked and
+submitted. This can be used to modify the comment (in place) with posting
+details or other such actions.
+
+If any receiver returns ``False`` the comment will be discarded and a 403 (not
+allowed) response will be returned.
+
+This signal is sent at more or less the same time (just before, actually) as the
+``Comment`` object's :data:`~django.db.models.signals.pre_save` signal.
+
+Arguments sent with this signal:
+
+ ``sender``
+ The comment model.
+
+ ``comment``
+ The comment instance about to be posted. Note that it won't have been
+ saved into the database yet, so it won't have a primary key, and any
+ relations might not work correctly yet.
+
+ ``request``
+ The :class:`~django.http.HttpRequest` that posted the comment.
+
+comment_was_posted
+==================
+
+.. data:: django.contrib.comments.signals.comment_was_posted
+ :module:
+
+Sent just after the comment is saved.
+
+Arguments sent with this signal:
+
+ ``sender``
+ The comment model.
+
+ ``comment``
+ The comment instance that was posted. Note that it will have already
+ been saved, so if you modify it you'll need to call
+ :meth:`~django.db.models.Model.save` again.
+
+ ``request``
+ The :class:`~django.http.HttpRequest` that posted the comment.
+
+comment_was_flagged
+===================
+
+.. data:: django.contrib.comments.signals.comment_was_flagged
+ :module:
+
+Sent after a comment was "flagged" in some way. Check the flag to see if this
+was a user requesting removal of a comment, a moderator approving/removing a
+comment, or some other custom user flag.
+
+Arguments sent with this signal:
+
+ ``sender``
+ The comment model.
+
+ ``comment``
+ The comment instance that was posted. Note that it will have already
+ been saved, so if you modify it you'll need to call
+ :meth:`~django.db.models.Model.save` again.
+
+ ``flag``
+ The :class:`~django.contrib.comments.models.CommentFlag` that's been
+ attached to the comment.
+
+ ``created``
+ ``True`` if this is a new flag; ``False`` if it's a duplicate flag.
+
+ ``request``
+ The :class:`~django.http.HttpRequest` that posted the comment.
diff --git a/parts/django/docs/ref/contrib/comments/upgrade.txt b/parts/django/docs/ref/contrib/comments/upgrade.txt
new file mode 100644
index 0000000..3d6b5af
--- /dev/null
+++ b/parts/django/docs/ref/contrib/comments/upgrade.txt
@@ -0,0 +1,78 @@
+===============================================
+Upgrading from Django's previous comment system
+===============================================
+
+Prior versions of Django included an outdated, undocumented comment system. Users who reverse-engineered this framework will need to upgrade to use the
+new comment system; this guide explains how.
+
+The main changes from the old system are:
+
+ * This new system is documented.
+
+ * It uses modern Django features like :doc:`forms </topics/forms/index>` and
+ :doc:`modelforms </topics/forms/modelforms>`.
+
+ * It has a single ``Comment`` model instead of separate ``FreeComment`` and
+ ``Comment`` models.
+
+ * Comments have "email" and "URL" fields.
+
+ * No ratings, photos and karma. This should only effect World Online.
+
+ * The ``{% comment_form %}`` tag no longer exists. Instead, there's now two
+ functions: ``{% get_comment_form %}``, which returns a form for posting a
+ new comment, and ``{% render_comment_form %}``, which renders said form
+ using the ``comments/form.html`` template.
+
+ * The way comments are include in your URLconf have changed; you'll need to
+ replace::
+
+ (r'^comments/', include('django.contrib.comments.urls.comments')),
+
+ with::
+
+ (r'^comments/', include('django.contrib.comments.urls')),
+
+Upgrading data
+--------------
+
+The data models for Django's comment system have changed, as have the
+table names. Before you transfer your existing data into the new comments
+system, make sure that you have installed the new comments system as
+explained in the
+:doc:`quick start guide </ref/contrib/comments/index>`.
+This will ensure that the new tables have been properly created.
+
+To transfer your data into the new comments system, you'll need to directly
+run the following SQL:
+
+.. code-block:: sql
+
+ BEGIN;
+
+ INSERT INTO django_comments
+ (content_type_id, object_pk, site_id, user_name, user_email, user_url,
+ comment, submit_date, ip_address, is_public, is_removed)
+ SELECT
+ content_type_id, object_id, site_id, person_name, '', '', comment,
+ submit_date, ip_address, is_public, not approved
+ FROM comments_freecomment;
+
+ INSERT INTO django_comments
+ (content_type_id, object_pk, site_id, user_id, user_name, user_email,
+ user_url, comment, submit_date, ip_address, is_public, is_removed)
+ SELECT
+ content_type_id, object_id, site_id, user_id, '', '', '', comment,
+ submit_date, ip_address, is_public, is_removed
+ FROM comments_comment;
+
+ UPDATE django_comments SET user_name = (
+ SELECT username FROM auth_user
+ WHERE django_comments.user_id = auth_user.id
+ ) WHERE django_comments.user_id is not NULL;
+ UPDATE django_comments SET user_email = (
+ SELECT email FROM auth_user
+ WHERE django_comments.user_id = auth_user.id
+ ) WHERE django_comments.user_id is not NULL;
+
+ COMMIT;
diff --git a/parts/django/docs/ref/contrib/contenttypes.txt b/parts/django/docs/ref/contrib/contenttypes.txt
new file mode 100644
index 0000000..b695651
--- /dev/null
+++ b/parts/django/docs/ref/contrib/contenttypes.txt
@@ -0,0 +1,385 @@
+==========================
+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 :lookup:`a get() lookup <get>` 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>]
+
+Just as :class:`django.contrib.contenttypes.generic.GenericForeignKey`
+accepts the names of the content-type and object-ID fields as
+arguments, so too does ``GenericRelation``; if the model which has the
+generic foreign key is using non-default names for those fields, you
+must pass the names of the fields when setting up a
+``GenericRelation`` to it. For example, if the ``TaggedItem`` model
+referred to above used fields named ``content_type_fk`` and
+``object_primary_key`` to create its generic foreign key, then a
+``GenericRelation`` back to it would need to be defined like so::
+
+ tags = generic.GenericRelation(TaggedItem, content_type_field='content_type_fk', object_id_field='object_primary_key')
+
+Of course, if you don't add the reverse relationship, you can do the
+same types of lookups 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 the model in a
+:class:`~django.contrib.contenttypes.generic.GenericRelation` uses a
+non-default value for ``ct_field`` or ``fk_field`` in its
+:class:`~django.contrib.contenttypes.generic.GenericForeignKey` (e.g. the
+:mod:`django.contrib.comments` app uses ``ct_field="object_pk"``),
+you'll need to set ``content_type_field`` and/or ``object_id_field`` in
+the :class:`~django.contrib.contenttypes.generic.GenericRelation` to
+match the ``ct_field`` and ``fk_field``, respectively, in the
+:class:`~django.contrib.contenttypes.generic.GenericForeignKey`::
+
+ comments = generic.GenericRelation(Comment, object_id_field="object_pk")
+
+Note also, 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 and aggregation
+---------------------------------
+
+:doc:`Django's database aggregation API </topics/db/aggregation>`
+doesn't work with a
+:class:`~django.contrib.contenttypes.generic.GenericRelation`. For example, you
+might be tempted to try something like::
+
+ Bookmark.objects.aggregate(Count('tags'))
+
+This will not work correctly, however. The generic relation adds extra filters
+to the queryset to ensure the correct content type, but the ``aggregate`` method
+doesn't take them into account. For now, if you need aggregates on generic
+relations, you'll need to calculate them without using the aggregation API.
+
+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
+:doc:`model formset </topics/forms/modelforms>` and
+:doc:`admin </ref/contrib/admin/index>` documentation for more information.
+
+.. class:: generic.GenericInlineModelAdmin
+
+ The :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`
+ class inherits all properties from an
+ :class:`~django.contrib.admin.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``.
diff --git a/parts/django/docs/ref/contrib/csrf.txt b/parts/django/docs/ref/contrib/csrf.txt
new file mode 100644
index 0000000..c32dd73
--- /dev/null
+++ b/parts/django/docs/ref/contrib/csrf.txt
@@ -0,0 +1,433 @@
+=====================================
+Cross Site Request Forgery protection
+=====================================
+
+.. module:: django.middleware.csrf
+ :synopsis: Protects against Cross Site Request Forgeries
+
+The CSRF middleware and template tag provides easy-to-use protection against
+`Cross Site Request Forgeries`_. This type of attack occurs when a malicious
+Web site contains a link, a form button or some javascript that is intended to
+perform some action on your Web site, using the credentials of a logged-in user
+who visits the malicious site in their browser. A related type of attack,
+'login CSRF', where an attacking site tricks a user's browser into logging into
+a site with someone else's credentials, is also covered.
+
+The first defense against CSRF attacks is to ensure that GET requests are
+side-effect free. POST requests can then be protected by following the steps
+below.
+
+.. versionadded:: 1.2
+ The 'contrib' apps, including the admin, use the functionality described
+ here. Because it is security related, a few things have been added to core
+ functionality to allow this to happen without any required upgrade steps.
+
+.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
+
+How to use it
+=============
+
+.. versionchanged:: 1.2
+ The template tag functionality (the recommended way to use this) was added
+ in version 1.2. The previous method (still available) is described under
+ `Legacy method`_.
+
+To enable CSRF protection for your views, follow these steps:
+
+ 1. Add the middleware
+ ``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of
+ middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come
+ before ``CsrfResponseMiddleware`` if that is being used, and before any
+ view middleware that assume that CSRF attacks have been dealt with.)
+
+ Alternatively, you can use the decorator
+ ``django.views.decorators.csrf.csrf_protect`` on particular views you
+ want to protect (see below).
+
+ 2. In any template that uses a POST form, use the :ttag:`csrf_token` tag inside
+ the ``<form>`` element if the form is for an internal URL, e.g.::
+
+ <form action="" method="post">{% csrf_token %}
+
+ This should not be done for POST forms that target external URLs, since
+ that would cause the CSRF token to be leaked, leading to a vulnerability.
+
+ 3. In the corresponding view functions, ensure that the
+ ``'django.core.context_processors.csrf'`` context processor is
+ being used. Usually, this can be done in one of two ways:
+
+ 1. Use RequestContext, which always uses
+ ``'django.core.context_processors.csrf'`` (no matter what your
+ TEMPLATE_CONTEXT_PROCESSORS setting). If you are using
+ generic views or contrib apps, you are covered already, since these
+ apps use RequestContext throughout.
+
+ 2. Manually import and use the processor to generate the CSRF token and
+ add it to the template context. e.g.::
+
+ from django.core.context_processors import csrf
+ from django.shortcuts import render_to_response
+
+ def my_view(request):
+ c = {}
+ c.update(csrf(request))
+ # ... view code here
+ return render_to_response("a_template.html", c)
+
+ You may want to write your own ``render_to_response`` wrapper that
+ takes care of this step for you.
+
+The utility script ``extras/csrf_migration_helper.py`` can help to automate the
+finding of code and templates that may need to be upgraded. It contains full
+help on how to use it.
+
+The decorator method
+--------------------
+
+Rather than adding ``CsrfViewMiddleware`` as a blanket protection, you can use
+the ``csrf_protect`` decorator, which has exactly the same functionality, on
+particular views that need the protection. It must be used **both** on views
+that insert the CSRF token in the output, and on those that accept the POST form
+data. (These are often the same view function, but not always). It is used like
+this::
+
+ from django.views.decorators.csrf import csrf_protect
+ from django.template import RequestContext
+
+ @csrf_protect
+ def my_view(request):
+ c = {}
+ # ...
+ return render_to_response("a_template.html", c,
+ context_instance=RequestContext(request))
+
+Use of the decorator is **not recommended** by itself, since if you forget to
+use it, you will have a security hole. The 'belt and braces' strategy of using
+both is fine, and will incur minimal overhead.
+
+Legacy method
+-------------
+
+In Django 1.1, the template tag did not exist. Instead, a post-processing
+middleware that re-wrote POST forms to include the CSRF token was used. If you
+are upgrading a site from version 1.1 or earlier, please read this section and
+the `Upgrading notes`_ below. The post-processing middleware is still available
+as ``CsrfResponseMiddleware``, and it can be used by following these steps:
+
+ 1. Follow step 1 above to install ``CsrfViewMiddleware``.
+
+ 2. Add ``'django.middleware.csrf.CsrfResponseMiddleware'`` to your
+ :setting:`MIDDLEWARE_CLASSES` setting.
+
+ ``CsrfResponseMiddleware`` needs to process the response before things
+ like compression or setting ofETags happen to the response, so it must
+ come after ``GZipMiddleware``, ``CommonMiddleware`` and
+ ``ConditionalGetMiddleware`` in the list. It also must come after
+ ``CsrfViewMiddleware``.
+
+Use of the ``CsrfResponseMiddleware`` is not recommended because of the
+performance hit it imposes, and because of a potential security problem (see
+below). It can be used as an interim measure until applications have been
+updated to use the :ttag:`csrf_token` tag. It is deprecated and will be
+removed in Django 1.4.
+
+Django 1.1 and earlier provided a single ``CsrfMiddleware`` class. This is also
+still available for backwards compatibility. It combines the functions of the
+two middleware.
+
+Note also that previous versions of these classes depended on the sessions
+framework, but this dependency has now been removed, with backward compatibility
+support so that upgrading will not produce any issues.
+
+Security of legacy method
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST
+forms (unless the view has been decorated with ``csrf_response_exempt``). If
+the POST form has an external untrusted site as its target, rather than an
+internal page, that site will be sent the CSRF token when the form is submitted.
+Armed with this leaked information, that site will then be able to successfully
+launch a CSRF attack on your site against that user. The
+``@csrf_response_exempt`` decorator can be used to fix this, but only if the
+page doesn't also contain internal forms that require the token.
+
+.. _ref-csrf-upgrading-notes:
+
+Upgrading notes
+---------------
+
+When upgrading to version 1.2 or later, you may have applications that rely on
+the old post-processing functionality for CSRF protection, or you may not have
+enabled any CSRF protection. This section outlines the steps necessary for a
+smooth upgrade, without having to fix all the applications to use the new
+template tag method immediately.
+
+First of all, the location of the middleware and related functions have
+changed. There are backwards compatible stub files so that old imports will
+continue to work for now, but they are deprecated and will be removed in Django
+1.4. The following changes have been made:
+
+ * Middleware have been moved to ``django.middleware.csrf``
+ * Decorators have been moved to ``django.views.decorators.csrf``
+
+====================================================== ==============================================
+ Old New
+====================================================== ==============================================
+django.contrib.csrf.middleware.CsrfMiddleware django.middleware.csrf.CsrfMiddleware
+django.contrib.csrf.middleware.CsrfViewMiddleware django.middleware.csrf.CsrfViewMiddleware
+django.contrib.csrf.middleware.CsrfResponseMiddleware django.middleware.csrf.CsrfResponseMiddleware
+django.contrib.csrf.middleware.csrf_exempt django.views.decorators.csrf.csrf_exempt
+django.contrib.csrf.middleware.csrf_view_exempt django.views.decorators.csrf.csrf_view_exempt
+django.contrib.csrf.middleware.csrf_response_exempt django.views.decorators.csrf.csrf_response_exempt
+====================================================== ==============================================
+
+You should update any imports, and also the paths in your
+:setting:`MIDDLEWARE_CLASSES`.
+
+If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now
+have a working installation with CSRF protection. It is recommended at this
+point that you replace ``CsrfMiddleware`` with its two components,
+``CsrfViewMiddleware`` and ``CsrfResponseMiddleware`` (in that order).
+
+If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`,
+you will have a working installation but without any CSRF protection for your
+views (just as you had before). It is strongly recommended to install
+``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``, as described above.
+
+Note that contrib apps, such as the admin, have been updated to use the
+``csrf_protect`` decorator, so that they are secured even if you do not add the
+``CsrfViewMiddleware`` to your settings. However, if you have supplied
+customised templates to any of the view functions of contrib apps (whether
+explicitly via a keyword argument, or by overriding built-in templates), **you
+MUST update them** to include the :ttag:`csrf_token` template tag as described
+above, or they will stop working. (If you cannot update these templates for
+some reason, you will be forced to use ``CsrfResponseMiddleware`` for these
+views to continue working).
+
+Note also, if you are using the comments app, and you are not going to add
+``CsrfViewMiddleware`` to your settings (not recommended), you will need to add
+the ``csrf_protect`` decorator to any views that include the comment forms and
+target the comment views (usually using the :ttag:`comment_form_target` template
+tag).
+
+Assuming you have followed the above, all views in your Django site will now be
+protected by the ``CsrfViewMiddleware``. Contrib apps meet the requirements
+imposed by the ``CsrfViewMiddleware`` using the template tag, and other
+applications in your project will meet its requirements by virtue of the
+``CsrfResponseMiddleware``.
+
+The next step is to update all your applications to use the template tag, as
+described in `How to use it`_, steps 2-3. This can be done as soon as is
+practical. Any applications that are updated will now require Django 1.1.2 or
+later, since they will use the CSRF template tag which was not available in
+earlier versions. (The template tag in 1.1.2 is actually a no-op that exists
+solely to ease the transition to 1.2 — it allows apps to be created that have
+CSRF protection under 1.2 without requiring users of the apps to upgrade to the
+Django 1.2.X series).
+
+The utility script ``extras/csrf_migration_helper.py`` can help to automate the
+finding of code and templates that may need to be upgraded. It contains full
+help on how to use it.
+
+Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be
+removed from your settings.
+
+While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt``
+decorator, described in `Exceptions`_, may be useful. The post-processing
+middleware imposes a performance hit and a potential vulnerability, and any
+views that have been upgraded to use the new template tag method no longer need
+it.
+
+Exceptions
+----------
+
+.. versionadded:: 1.1
+.. versionchanged:: 1.2
+ Import paths for the decorators below were changed.
+
+To manually exclude a view function from being handled by either of the two CSRF
+middleware, you can use the ``csrf_exempt`` decorator, found in the
+``django.views.decorators.csrf`` module. For example::
+
+ from django.views.decorators.csrf import csrf_exempt
+
+ @csrf_exempt
+ def my_view(request):
+ return HttpResponse('Hello world')
+
+Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a
+``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found
+in the same module. These disable the view protection mechanism
+(``CsrfViewMiddleware``) and the response post-processing
+(``CsrfResponseMiddleware``) respectively. They can be used individually if
+required.
+
+You don't have to worry about doing this for most AJAX views. Any request sent
+with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the `How
+it works`_ section.)
+
+Subdomains
+----------
+
+By default, CSRF cookies are specific to the subdomain they are set for. This
+means that a form served from one subdomain (e.g. server1.example.com) will not
+be able to have a target on another subdomain (e.g. server2.example.com). This
+restriction can be removed by setting :setting:`CSRF_COOKIE_DOMAIN` to be
+something like ``".example.com"``.
+
+Please note that, with or without use of this setting, this CSRF protection
+mechanism is not safe against cross-subdomain attacks -- see `Limitations`_.
+
+Rejected requests
+=================
+
+By default, a '403 Forbidden' response is sent to the user if an incoming
+request fails the checks performed by ``CsrfViewMiddleware``. This should
+usually only be seen when there is a genuine Cross Site Request Forgery, or
+when, due to a programming error, the CSRF token has not been included with a
+POST form.
+
+No logging is done, and the error message is not very friendly, so you may want
+to provide your own page for handling this condition. To do this, simply set
+the :setting:`CSRF_FAILURE_VIEW` setting to a dotted path to your own view
+function, which should have the following signature::
+
+ def csrf_failure(request, reason="")
+
+where ``reason`` is a short message (intended for developers or logging, not for
+end users) indicating the reason the request was rejected.
+
+How it works
+============
+
+The CSRF protection is based on the following things:
+
+1. A CSRF cookie that is set to a random value (a session independent nonce, as
+ it is called), which other sites will not have access to.
+
+ This cookie is set by ``CsrfViewMiddleware``. It is meant to be permanent,
+ but since there is no way to set a cookie that never expires, it is sent with
+ every response that has called ``django.middleware.csrf.get_token()``
+ (the function used internally to retrieve the CSRF token).
+
+2. A hidden form field with the name 'csrfmiddlewaretoken' present in all
+ outgoing POST forms. The value of this field is the value of the CSRF
+ cookie.
+
+ This part is done by the template tag (and with the legacy method, it is done
+ by ``CsrfResponseMiddleware``).
+
+3. For all incoming POST requests, a CSRF cookie must be present, and the
+ 'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
+ user will get a 403 error.
+
+ This check is done by ``CsrfViewMiddleware``.
+
+4. In addition, for HTTPS requests, strict referer checking is done by
+ ``CsrfViewMiddleware``. This is necessary to address a Man-In-The-Middle
+ attack that is possible under HTTPS when using a session independent nonce,
+ due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
+ by clients that are talking to a site under HTTPS. (Referer checking is not
+ done for HTTP requests because the presence of the Referer header is not
+ reliable enough under HTTP.)
+
+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.
+
+``CsrfResponseMiddleware`` checks the Content-Type before modifying the
+response, and only pages that are served as 'text/html' or
+'application/xml+xhtml' are modified.
+
+AJAX
+----
+
+The middleware tries to be smart about requests that come in via AJAX. Most
+modern JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP
+header; these requests are detected and automatically *not* handled by this
+middleware. We can do this safely because, in the context of a browser, the
+header can only be added by using ``XMLHttpRequest``, and browsers already
+implement a same-domain policy for ``XMLHttpRequest``.
+
+For the more recent browsers that relax this same-domain policy, custom headers
+like "X-Requested-With" are only allowed after the browser has done a
+'preflight' check to the server to see if the cross-domain request is allowed,
+using a strictly 'opt in' mechanism, so the exception for AJAX is still safe—if
+the developer has specifically opted in to allowing cross-site AJAX POST
+requests on a specific URL, they obviously don't want the middleware to disallow
+exactly that.
+
+.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+
+Caching
+=======
+
+If the :ttag:`csrf_token` template tag is used by a template (or the ``get_token``
+function is called some other way), ``CsrfViewMiddleware`` will add a cookie and
+a ``Vary: Cookie`` header to the response. Similarly,
+``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted
+a token. This means that these middleware will play well with the cache
+middleware if it is used as instructed (``UpdateCacheMiddleware`` goes before
+all other middleware).
+
+However, if you use cache decorators on individual views, the CSRF middleware
+will not yet have been able to set the Vary header. In this case, on any views
+that will require a CSRF token to be inserted you should use the
+:func:`django.views.decorators.vary.vary_on_cookie` decorator first::
+
+ from django.views.decorators.cache import cache_page
+ from django.views.decorators.vary import vary_on_cookie
+
+ @cache_page(60 * 15)
+ @vary_on_cookie
+ def my_view(request):
+ # ...
+
+
+Testing
+=======
+
+The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view
+functions, due to the need for the CSRF token which must be sent with every POST
+request. For this reason, Django's HTTP client for tests has been modified to
+set a flag on requests which relaxes the middleware and the ``csrf_protect``
+decorator so that they no longer rejects requests. In every other respect
+(e.g. sending cookies etc.), they behave the same.
+
+If, for some reason, you *want* the test client to perform CSRF
+checks, you can create an instance of the test client that enforces
+CSRF checks::
+
+ >>> from django.test import Client
+ >>> csrf_client = Client(enforce_csrf_checks=True)
+
+Limitations
+===========
+
+Subdomains within a site will be able to set cookies on the client for the whole
+domain. By setting the cookie and using a corresponding token, subdomains will
+be able to circumvent the CSRF protection. The only way to avoid this is to
+ensure that subdomains are controlled by trusted users (or, are at least unable
+to set cookies). Note that even without CSRF, there are other vulnerabilities,
+such as session fixation, that make giving subdomains to untrusted parties a bad
+idea, and these vulnerabilities cannot easily be fixed with current browsers.
+
+If you are using ``CsrfResponseMiddleware`` and 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. You should
+use the template tag or :meth:`django.middleware.csrf.get_token` to get
+the CSRF token and ensure it is included when your form is submitted.
+
+Contrib and reusable apps
+=========================
+
+Because it is possible for the developer to turn off the ``CsrfViewMiddleware``,
+all relevant views in contrib apps use the ``csrf_protect`` decorator to ensure
+the security of these applications against CSRF. It is recommended that the
+developers of other reusable apps that want the same guarantees also use the
+``csrf_protect`` decorator on their views.
diff --git a/parts/django/docs/ref/contrib/databrowse.txt b/parts/django/docs/ref/contrib/databrowse.txt
new file mode 100644
index 0000000..33c8228
--- /dev/null
+++ b/parts/django/docs/ref/contrib/databrowse.txt
@@ -0,0 +1,90 @@
+==========
+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 :doc:`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 :doc:`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 :doc:`URLconf
+</topics/http/urls>`, as described in the :doc:`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
+:doc:`user authentication docs </ref/contrib/auth>` provide full details and a
+sample template that can be used for this purpose.
diff --git a/parts/django/docs/ref/contrib/flatpages.txt b/parts/django/docs/ref/contrib/flatpages.txt
new file mode 100644
index 0000000..46b28dc
--- /dev/null
+++ b/parts/django/docs/ref/contrib/flatpages.txt
@@ -0,0 +1,167 @@
+=================
+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.
+
+.. versionadded:: 1.0
+
+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.lawrence.com/about/contact/
+ * http://www2.ljworld.com/site/rules/
+
+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.
+
+ Also make sure you've correctly set :setting:`SITE_ID` to the ID of the
+ site the settings file represents. This will usually be ``1`` (i.e.
+ ``SITE_ID = 1``, but if you're using the sites framework to manage
+ multiple sites, it could be the ID of a different site.
+
+ 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.
+
+.. admonition:: Flatpages will not apply view middleware
+
+ Because the ``FlatpageFallbackMiddleware`` is applied only after
+ URL resolution has failed and produced a 404, the response it
+ returns will not apply any :ref:`view middleware <view-middleware>`
+ methods. Only requests which are successfully routed to a view via
+ normal URL resolution apply view middleware.
+
+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 :doc:`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
+ :doc:`Django model </topics/db/models>`,
+ which lives in `django/contrib/flatpages/models.py`_. You can access
+ flatpage objects via the :doc:`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: in the admin, a collapsed fieldset titled
+"Advanced options" (clicking will expand it) contains a field for
+specifying a template name. If you're creating a flat page via the
+Python API you can simply set the template name as the field
+``template_name`` on the ``FlatPage`` object.
+
+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.
diff --git a/parts/django/docs/ref/contrib/formtools/form-preview.txt b/parts/django/docs/ref/contrib/formtools/form-preview.txt
new file mode 100644
index 0000000..a2cbea7
--- /dev/null
+++ b/parts/django/docs/ref/contrib/formtools/form-preview.txt
@@ -0,0 +1,121 @@
+============
+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.forms import SomeModelForm
+ 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.
+
+Advanced ``FormPreview`` methods
+================================
+
+.. versionadded:: 1.2
+
+.. method:: FormPreview.process_preview
+
+ Given a validated form, performs any extra processing before displaying the
+ preview page, and saves any extra data in context.
+
+ By default, this method is empty. It is called after the form is validated,
+ but before the context is modified with hash information and rendered.
diff --git a/parts/django/docs/ref/contrib/formtools/form-wizard.txt b/parts/django/docs/ref/contrib/formtools/form-wizard.txt
new file mode 100644
index 0000000..390d575
--- /dev/null
+++ b/parts/django/docs/ref/contrib/formtools/form-wizard.txt
@@ -0,0 +1,312 @@
+===========
+Form wizard
+===========
+
+.. module:: django.contrib.formtools.wizard
+ :synopsis: Splits forms across multiple Web pages.
+
+.. versionadded:: 1.0
+
+Django comes with an optional "form wizard" application that splits
+:doc:`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 :class:`~django.forms.Form` classes -- one per wizard
+ page.
+
+ 2. Create a :class:`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:`FormWizard` class.
+
+Defining ``Form`` classes
+=========================
+
+The first step in creating a form wizard is to create the
+:class:`~django.forms.Form` classes. These should be standard
+:class:`django.forms.Form` classes, covered in the :doc:`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.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` subclass. As with your
+:class:`~django.forms.Form` classes, this :class:`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:`~FormWizard.done()` method.
+
+.. method:: FormWizard.done
+
+ This method 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 :class:`~django.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:`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:`~FormWizard.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.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 :tfilter:`safe` template filter,
+ to prevent auto-escaping, because it's raw HTML.
+
+You can supply extra context to this template in two ways:
+
+ * Set the :attr:`~FormWizard.extra_context` attribute on your
+ :class:`FormWizard` subclass to a dictionary.
+
+ * Pass a dictionary as a parameter named ``extra_context`` to your wizard's
+ URL pattern in your URLconf. See :ref:`hooking-wizard-into-urlconf`.
+
+Here's a full example template:
+
+.. code-block:: html+django
+
+ {% extends "base.html" %}
+
+ {% block content %}
+ <p>Step {{ step }} of {{ step_count }}</p>
+ <form action="." method="post">{% csrf_token %}
+ <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-wizard-into-urlconf:
+
+Hooking the wizard into a URLconf
+=================================
+
+Finally, we need to specify which forms to use in the wizard, and then
+deploy the new :class:`FormWizard` object a URL in ``urls.py``. The
+wizard takes a list of your :class:`~django.forms.Form` objects as
+arguments when you instantiate the Wizard::
+
+ from django.conf.urls.defaults import *
+ from testapp.forms import ContactForm1, ContactForm2, ContactWizard
+
+ urlpatterns = patterns('',
+ (r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
+ )
+
+Advanced ``FormWizard`` methods
+===============================
+
+.. class:: FormWizard
+
+ Aside from the :meth:`~done()` method, :class:`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 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.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.
+ 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']
+
+.. method:: FormWizard.render_template
+
+ Renders the template for the given step, returning an
+ :class:`~django.http.HttpResponse` 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.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):
+ # ...
diff --git a/parts/django/docs/ref/contrib/formtools/index.txt b/parts/django/docs/ref/contrib/formtools/index.txt
new file mode 100644
index 0000000..f364706
--- /dev/null
+++ b/parts/django/docs/ref/contrib/formtools/index.txt
@@ -0,0 +1,10 @@
+django.contrib.formtools
+========================
+
+A set of high-level abstractions for Django forms (:mod:`django.forms`).
+
+.. toctree::
+ :maxdepth: 1
+
+ form-preview
+ form-wizard
diff --git a/parts/django/docs/ref/contrib/gis/admin.txt b/parts/django/docs/ref/contrib/gis/admin.txt
new file mode 100644
index 0000000..011bb6b
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/admin.txt
@@ -0,0 +1,72 @@
+.. _ref-gis-admin:
+
+======================
+GeoDjango's admin site
+======================
+
+.. module:: django.contrib.gis.admin
+ :synopsis: GeoDjango's extensions to the admin site.
+
+
+``GeoModelAdmin``
+=================
+
+.. class:: GeoModelAdmin
+
+ .. attribute:: default_lon
+
+ The default center longitude.
+
+ .. attribute:: default_lat
+
+ The default center latitude.
+
+ .. attribute:: default_zoom
+
+ The default zoom level to use. Defaults to 18.
+
+ .. attribute:: extra_js
+
+ Sequence of URLs to any extra JavaScript to include.
+
+ .. attribute:: map_template
+
+ Override the template used to generate the JavaScript slippy map.
+ Default is ``'gis/admin/openlayers.html'``.
+
+ .. attribute:: map_width
+
+ Width of the map, in pixels. Defaults to 600.
+
+ .. attribute:: map_height
+
+ Height of the map, in pixels. Defaults to 400.
+
+ .. attribute:: openlayers_url
+
+ Link to the URL of the OpenLayers JavaScript. Defaults to
+ ``'http://openlayers.org/api/2.8/OpenLayers.js'``.
+
+
+ .. attribute:: modifiable
+
+ Defaults to ``False``. When set to to ``True``, disables editing of
+ existing geometry fields in the admin.
+
+ .. note::
+
+ This is different from adding the geometry field to
+ :attr:`~django.contrib.admin.ModelAdmin.readonly_fields`,
+ which will only display the WKT of the geometry. Setting
+ ``modifiable=False``, actually displays the geometry in a map,
+ but disables the ability to edit its vertices.
+
+``OSMGeoAdmin``
+===============
+
+.. class:: OSMGeoAdmin
+
+ A subclass of :class:`GeoModelAdmin` that uses a spherical mercator projection
+ with `OpenStreetMap <http://openstreetmap.org/>`_ street data tiles.
+ See the :ref:`OSMGeoAdmin introduction <osmgeoadmin-intro>`
+ in the tutorial for a usage example.
diff --git a/parts/django/docs/ref/contrib/gis/commands.txt b/parts/django/docs/ref/contrib/gis/commands.txt
new file mode 100644
index 0000000..3dd161c
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/commands.txt
@@ -0,0 +1,83 @@
+.. ref-geodjango-admin:
+
+=============================
+GeoDjango Management Commands
+=============================
+
+inspectdb
+=========
+
+.. describe:: django-admin.py inspectdb
+
+When :mod:`django.contrib.gis` is in your :setting:`INSTALLED_APPS`, the
+:djadmin:`inspectdb` management command is overridden with one from GeoDjango.
+The overridden command is spatially-aware, and places geometry fields in the
+auto-generated model definition, where appropriate.
+
+ogrinspect <data_source> <model_name>
+=====================================
+
+.. django-admin:: ogrinspect
+
+The ``ogrinpsect`` management command will inspect the given OGR-compatible
+:class:`~django.contrib.gis.gdal.DataSource` (e.g., a shapefile) and will
+output a GeoDjango model with the given model name. There's a detailed example
+of using ``ogrinspect`` :ref:`in the tutorial <ogrinspect-intro>`.
+
+.. django-admin-option:: --blank <blank_field(s)>
+
+ Use a comma separated list of OGR field names to add the ``blank=True``
+ keyword option to the field definition. Set with ``true`` to apply
+ to all applicable fields.
+
+.. django-admin-option:: --decimal <decimal_field(s)>
+
+ Use a comma separated list of OGR float fields to generate
+ :class:`~django.db.models.DecimalField` instead of the default
+ :class:`~django.db.models.FloatField`. Set to ``true`` to apply to all
+ OGR float fields.
+
+.. django-admin-option:: --geom-name <name>
+
+ Specifies the model attribute name to use for the geometry field.
+ Defaults to ``'geom'``.
+
+.. django-admin-option:: --layer <layer>
+
+ The key for specifying which layer in the OGR
+ :class:`~django.contrib.gis.gdal.DataSource` source to use.
+ Defaults to 0 (the first layer). May be an integer or a string identifier
+ for the :class:`~django.contrib.gis.gdal.Layer`.
+
+.. django-admin-option:: --mapping
+
+ Automatically generate a mapping dictionary for use with
+ :class:`~django.contrib.gis.utils.LayerMapping`.
+
+.. django-admin-option:: --multi-geom
+
+ When generating the geometry field, treat it as a geometry collection.
+ For example, if this setting is enabled then a
+ :class:`~django.contrib.gis.db.models.MultiPolygonField` will be placed
+ in the generated model rather than
+ :class:`~django.contrib.gis.db.models.PolygonField`.
+
+.. django-admin-option:: --name-field <name_field>
+
+ Generates a ``__unicode__`` routine on the model that will return the
+ the given field name.
+
+.. django-admin-option:: --no-imports
+
+ Suppresses the ``from django.contrib.gis.db import models`` import statement.
+
+.. django-admin-option:: --null <null_field(s)>
+
+ Use a comma separated list of OGR field names to add the ``null=True``
+ keyword option to the field definition. Set with ``true`` to apply to
+ all applicable fields.
+
+.. django-admin-option:: --srid
+
+ The SRID to use for the geometry field. If not set, ``ogrinspect`` attempts
+ to automatically determine of the SRID of the data source.
diff --git a/parts/django/docs/ref/contrib/gis/create_template_postgis-1.3.sh b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.3.sh
new file mode 100755
index 0000000..c9ab4fc
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.3.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+POSTGIS_SQL_PATH=`pg_config --sharedir`
+createdb -E UTF8 template_postgis # Create the template spatial database.
+createlang -d template_postgis plpgsql # Adding PLPGSQL language support.
+psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
+psql -d template_postgis -f $POSTGIS_SQL_PATH/lwpostgis.sql # Loading the PostGIS SQL routines
+psql -d template_postgis -f $POSTGIS_SQL_PATH/spatial_ref_sys.sql
+psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;" # Enabling users to alter spatial tables.
+psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
diff --git a/parts/django/docs/ref/contrib/gis/create_template_postgis-1.4.sh b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.4.sh
new file mode 100755
index 0000000..57a1373
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.4.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+POSTGIS_SQL_PATH=`pg_config --sharedir`/contrib
+createdb -E UTF8 template_postgis # Create the template spatial database.
+createlang -d template_postgis plpgsql # Adding PLPGSQL language support.
+psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
+psql -d template_postgis -f $POSTGIS_SQL_PATH/postgis.sql # Loading the PostGIS SQL routines
+psql -d template_postgis -f $POSTGIS_SQL_PATH/spatial_ref_sys.sql
+psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;" # Enabling users to alter spatial tables.
+psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
diff --git a/parts/django/docs/ref/contrib/gis/create_template_postgis-1.5.sh b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.5.sh
new file mode 100755
index 0000000..081b5f2
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/create_template_postgis-1.5.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+POSTGIS_SQL_PATH=`pg_config --sharedir`/contrib/postgis-1.5
+createdb -E UTF8 template_postgis # Create the template spatial database.
+createlang -d template_postgis plpgsql # Adding PLPGSQL language support.
+psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
+psql -d template_postgis -f $POSTGIS_SQL_PATH/postgis.sql # Loading the PostGIS SQL routines
+psql -d template_postgis -f $POSTGIS_SQL_PATH/spatial_ref_sys.sql
+psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;" # Enabling users to alter spatial tables.
+psql -d template_postgis -c "GRANT ALL ON geography_columns TO PUBLIC;"
+psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
diff --git a/parts/django/docs/ref/contrib/gis/create_template_postgis-debian.sh b/parts/django/docs/ref/contrib/gis/create_template_postgis-debian.sh
new file mode 100755
index 0000000..46bd074
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/create_template_postgis-debian.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+POSTGIS_SQL_PATH=/usr/share/postgresql-8.3-postgis
+createdb -E UTF8 template_postgis # Create the template spatial database.
+createlang -d template_postgis plpgsql # Adding PLPGSQL language support.
+psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
+psql -d template_postgis -f $POSTGIS_SQL_PATH/lwpostgis.sql # Loading the PostGIS SQL routines
+psql -d template_postgis -f $POSTGIS_SQL_PATH/spatial_ref_sys.sql
+psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;" # Enabling users to alter spatial tables.
+psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
diff --git a/parts/django/docs/ref/contrib/gis/db-api.txt b/parts/django/docs/ref/contrib/gis/db-api.txt
new file mode 100644
index 0000000..fbced8e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/db-api.txt
@@ -0,0 +1,349 @@
+.. _ref-gis-db-api:
+
+======================
+GeoDjango Database API
+======================
+
+.. module:: django.contrib.gis.db.models
+ :synopsis: GeoDjango's database API.
+
+.. _spatial-backends:
+
+Spatial Backends
+================
+
+.. versionadded:: 1.2
+
+In Django 1.2, support for :doc:`multiple databases </topics/db/multi-db>` was
+introduced. In order to support multiple databases, GeoDjango has segregated
+its functionality into full-fledged spatial database backends:
+
+* :mod:`django.contrib.gis.db.backends.postgis`
+* :mod:`django.contrib.gis.db.backends.mysql`
+* :mod:`django.contrib.gis.db.backends.oracle`
+* :mod:`django.contrib.gis.db.backends.spatialite`
+
+Database Settings Backwards-Compatibility
+-----------------------------------------
+
+In :doc:`Django 1.2 </releases/1.2>`, the way
+to :ref:`specify databases <specifying-databases>` in your settings was changed.
+The old database settings format (e.g., the ``DATABASE_*`` settings)
+is backwards compatible with GeoDjango, and will automatically use the
+appropriate spatial backend as long as :mod:`django.contrib.gis` is in
+your :setting:`INSTALLED_APPS`. For example, if you have the following in
+your settings::
+
+ DATABASE_ENGINE='postgresql_psycopg2'
+
+ ...
+
+ INSTALLED_APPS = (
+ ...
+ 'django.contrib.gis',
+ ...
+ )
+
+Then, :mod:`django.contrib.gis.db.backends.postgis` is automatically used as your
+spatial backend.
+
+.. _mysql-spatial-limitations:
+
+MySQL Spatial Limitations
+-------------------------
+
+MySQL's spatial extensions only support bounding box operations
+(what MySQL calls minimum bounding rectangles, or MBR). Specifically,
+`MySQL does not conform to the OGC standard <http://dev.mysql.com/doc/refman/5.1/en/functions-that-test-spatial-relationships-between-geometries.html>`_:
+
+ Currently, MySQL does not implement these functions
+ [``Contains``, ``Crosses``, ``Disjoint``, ``Intersects``, ``Overlaps``,
+ ``Touches``, ``Within``]
+ according to the specification. Those that are implemented return
+ the same result as the corresponding MBR-based functions.
+
+In other words, while spatial lookups such as :lookup:`contains <gis-contains>`
+are available in GeoDjango when using MySQL, the results returned are really
+equivalent to what would be returned when using :lookup:`bbcontains`
+on a different spatial backend.
+
+.. warning::
+
+ True spatial indexes (R-trees) are only supported with
+ MyISAM tables on MySQL. [#fnmysqlidx]_ In other words, when using
+ MySQL spatial extensions you have to choose between fast spatial
+ lookups and the integrity of your data -- MyISAM tables do
+ not support transactions or foreign key constraints.
+
+Creating and Saving Geographic Models
+=====================================
+Here is an example of how to create a geometry object (assuming the ``Zipcode``
+model)::
+
+ >>> from zipcode.models import Zipcode
+ >>> z = Zipcode(code=77096, poly='POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))')
+ >>> z.save()
+
+:class:`~django.contrib.gis.geos.GEOSGeometry` objects may also be used to save geometric models::
+
+ >>> from django.contrib.gis.geos import GEOSGeometry
+ >>> poly = GEOSGeometry('POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))')
+ >>> z = Zipcode(code=77096, poly=poly)
+ >>> z.save()
+
+Moreover, if the ``GEOSGeometry`` is in a different coordinate system (has a
+different SRID value) than that of the field, then it will be implicitly
+transformed into the SRID of the model's field, using the spatial database's
+transform procedure::
+
+ >>> poly_3084 = GEOSGeometry('POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))', srid=3084) # SRID 3084 is 'NAD83(HARN) / Texas Centric Lambert Conformal'
+ >>> z = Zipcode(code=78212, poly=poly_3084)
+ >>> z.save()
+ >>> from django.db import connection
+ >>> print connection.queries[-1]['sql'] # printing the last SQL statement executed (requires DEBUG=True)
+ INSERT INTO "geoapp_zipcode" ("code", "poly") VALUES (78212, ST_Transform(ST_GeomFromWKB('\\001 ... ', 3084), 4326))
+
+Thus, geometry parameters may be passed in using the ``GEOSGeometry`` object, WKT
+(Well Known Text [#fnwkt]_), HEXEWKB (PostGIS specific -- a WKB geometry in
+hexadecimal [#fnewkb]_), and GeoJSON [#fngeojson]_ (requires GDAL). Essentially,
+if the input is not a ``GEOSGeometry`` object, the geometry field will attempt to
+create a ``GEOSGeometry`` instance from the input.
+
+For more information creating :class:`~django.contrib.gis.geos.GEOSGeometry`
+objects, refer to the :ref:`GEOS tutorial <geos-tutorial>`.
+
+.. _spatial-lookups-intro:
+
+Spatial Lookups
+===============
+
+GeoDjango's lookup types may be used with any manager method like
+``filter()``, ``exclude()``, etc. However, the lookup types unique to
+GeoDjango are only available on geometry fields.
+Filters on 'normal' fields (e.g. :class:`~django.db.models.CharField`)
+may be chained with those on geographic fields. Thus, geographic queries
+take the following general form (assuming the ``Zipcode`` model used in the
+:ref:`ref-gis-model-api`)::
+
+ >>> qs = Zipcode.objects.filter(<field>__<lookup_type>=<parameter>)
+ >>> qs = Zipcode.objects.exclude(...)
+
+For example::
+
+ >>> qs = Zipcode.objects.filter(poly__contains=pnt)
+
+In this case, ``poly`` is the geographic field, :lookup:`contains <gis-contains>`
+is the spatial lookup type, and ``pnt`` is the parameter (which may be a
+:class:`~django.contrib.gis.geos.GEOSGeometry` object or a string of
+GeoJSON , WKT, or HEXEWKB).
+
+A complete reference can be found in the :ref:`spatial lookup reference
+<spatial-lookups>`.
+
+.. note::
+
+ GeoDjango constructs spatial SQL with the :class:`GeoQuerySet`, a
+ subclass of :class:`~django.db.models.QuerySet`. The
+ :class:`GeoManager` instance attached to your model is what
+ enables use of :class:`GeoQuerySet`.
+
+.. _distance-queries:
+
+Distance Queries
+================
+
+Introduction
+------------
+Distance calculations with spatial data is tricky because, unfortunately,
+the Earth is not flat. Some distance queries with fields in a geographic
+coordinate system may have to be expressed differently because of
+limitations in PostGIS. Please see the :ref:`selecting-an-srid` section
+in the :ref:`ref-gis-model-api` documentation for more details.
+
+.. _distance-lookups-intro:
+
+Distance Lookups
+----------------
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+The following distance lookups are available:
+
+* :lookup:`distance_lt`
+* :lookup:`distance_lte`
+* :lookup:`distance_gt`
+* :lookup:`distance_gte`
+* :lookup:`dwithin`
+
+.. note::
+
+ For *measuring*, rather than querying on distances, use the
+ :meth:`GeoQuerySet.distance` method.
+
+Distance lookups take a tuple parameter comprising:
+
+#. A geometry to base calculations from; and
+#. A number or :class:`~django.contrib.gis.measure.Distance` object containing the distance.
+
+If a :class:`~django.contrib.gis.measure.Distance` object is used,
+it may be expressed in any units (the SQL generated will use units
+converted to those of the field); otherwise, numeric parameters are assumed
+to be in the units of the field.
+
+.. note::
+
+ For users of PostGIS 1.4 and below, the routine ``ST_Distance_Sphere``
+ is used by default for calculating distances on geographic coordinate systems
+ (e.g., WGS84) -- which may only be called with point geometries [#fndistsphere14]_.
+ Thus, geographic distance lookups on traditional PostGIS geometry columns are
+ only allowed on :class:`PointField` model fields using a point for the
+ geometry parameter.
+
+.. note::
+
+ In PostGIS 1.5, ``ST_Distance_Sphere`` does *not* limit the geometry types
+ geographic distance queries are performed with. [#fndistsphere15]_ However,
+ these queries may take a long time, as great-circle distances must be
+ calculated on the fly for *every* row in the query. This is because the
+ spatial index on traditional geometry fields cannot be used.
+
+ For much better performance on WGS84 distance queries, consider using
+ :ref:`geography columns <geography-type>` in your database instead because
+ they are able to use their spatial index in distance queries.
+ You can tell GeoDjango to use a geography column by setting ``geography=True``
+ in your field definition.
+
+For example, let's say we have a ``SouthTexasCity`` model (from the
+`GeoDjango distance tests`__ ) on a *projected* coordinate system valid for cities
+in southern Texas::
+
+ from django.contrib.gis.db import models
+
+ class SouthTexasCity(models.Model):
+ name = models.CharField(max_length=30)
+ # A projected coordinate system (only valid for South Texas!)
+ # is used, units are in meters.
+ point = models.PointField(srid=32140)
+ objects = models.GeoManager()
+
+Then distance queries may be performed as follows::
+
+ >>> from django.contrib.gis.geos import *
+ >>> from django.contrib.gis.measure import D # ``D`` is a shortcut for ``Distance``
+ >>> from geoapp import SouthTexasCity
+ # Distances will be calculated from this point, which does not have to be projected.
+ >>> pnt = fromstr('POINT(-96.876369 29.905320)', srid=4326)
+ # If numeric parameter, units of field (meters in this case) are assumed.
+ >>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, 7000))
+ # Find all Cities within 7 km, > 20 miles away, and > 100 chains away (an obscure unit)
+ >>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, D(km=7)))
+ >>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(mi=20)))
+ >>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(chain=100)))
+
+__ http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/tests/distapp/models.py
+
+.. _compatibility-table:
+
+Compatibility Tables
+====================
+
+.. _spatial-lookup-compatibility:
+
+Spatial Lookups
+---------------
+
+The following table provides a summary of what spatial lookups are available
+for each spatial database backend.
+
+================================= ========= ======== ============ ==========
+Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite
+================================= ========= ======== ============ ==========
+:lookup:`bbcontains` X X X
+:lookup:`bboverlaps` X X X
+:lookup:`contained` X X X
+:lookup:`contains <gis-contains>` X X X X
+:lookup:`contains_properly` X
+:lookup:`coveredby` X X
+:lookup:`covers` X X
+:lookup:`crosses` X X
+:lookup:`disjoint` X X X X
+:lookup:`distance_gt` X X X
+:lookup:`distance_gte` X X X
+:lookup:`distance_lt` X X X
+:lookup:`distance_lte` X X X
+:lookup:`dwithin` X X
+:lookup:`equals` X X X X
+:lookup:`exact` X X X X
+:lookup:`intersects` X X X X
+:lookup:`overlaps` X X X X
+:lookup:`relate` X X X
+:lookup:`same_as` X X X X
+:lookup:`touches` X X X X
+:lookup:`within` X X X X
+:lookup:`left` X
+:lookup:`right` X
+:lookup:`overlaps_left` X
+:lookup:`overlaps_right` X
+:lookup:`overlaps_above` X
+:lookup:`overlaps_below` X
+:lookup:`strictly_above` X
+:lookup:`strictly_below` X
+================================= ========= ======== ============ ==========
+
+.. _geoqueryset-method-compatibility:
+
+``GeoQuerySet`` Methods
+-----------------------
+The following table provides a summary of what :class:`GeoQuerySet` methods
+are available on each spatial backend. Please note that MySQL does not
+support any of these methods, and is thus excluded from the table.
+
+==================================== ======= ====== ==========
+Method PostGIS Oracle SpatiaLite
+==================================== ======= ====== ==========
+:meth:`GeoQuerySet.area` X X X
+:meth:`GeoQuerySet.centroid` X X X
+:meth:`GeoQuerySet.collect` X
+:meth:`GeoQuerySet.difference` X X X
+:meth:`GeoQuerySet.distance` X X X
+:meth:`GeoQuerySet.envelope` X X
+:meth:`GeoQuerySet.extent` X X
+:meth:`GeoQuerySet.extent3d` X
+:meth:`GeoQuerySet.force_rhr` X
+:meth:`GeoQuerySet.geohash` X
+:meth:`GeoQuerySet.geojson` X
+:meth:`GeoQuerySet.gml` X X
+:meth:`GeoQuerySet.intersection` X X X
+:meth:`GeoQuerySet.kml` X
+:meth:`GeoQuerySet.length` X X X
+:meth:`GeoQuerySet.make_line` X
+:meth:`GeoQuerySet.mem_size` X
+:meth:`GeoQuerySet.num_geom` X X X
+:meth:`GeoQuerySet.num_points` X X X
+:meth:`GeoQuerySet.perimeter` X X
+:meth:`GeoQuerySet.point_on_surface` X X X
+:meth:`GeoQuerySet.reverse_geom` X X
+:meth:`GeoQuerySet.scale` X X
+:meth:`GeoQuerySet.snap_to_grid` X
+:meth:`GeoQuerySet.svg` X X
+:meth:`GeoQuerySet.sym_difference` X X X
+:meth:`GeoQuerySet.transform` X X X
+:meth:`GeoQuerySet.translate` X X
+:meth:`GeoQuerySet.union` X X X
+:meth:`GeoQuerySet.unionagg` X X X
+==================================== ======= ====== ==========
+
+.. rubric:: Footnotes
+.. [#fnwkt] *See* Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999), at Ch. 3.2.5, p. 3-11 (SQL Textual Representation of Geometry).
+.. [#fnewkb] *See* `PostGIS EWKB, EWKT and Canonical Forms <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#EWKB_EWKT>`_, PostGIS documentation at Ch. 4.1.2.
+.. [#fngeojson] *See* Howard Butler, Martin Daly, Allan Doyle, Tim Schaub, & Christopher Schmidt, `The GeoJSON Format Specification <http://geojson.org/geojson-spec.html>`_, Revision 1.0 (June 16, 2008).
+.. [#fndistsphere14] *See* `PostGIS 1.4 documentation <http://postgis.refractions.net/documentation/manual-1.4/ST_Distance_Sphere.html>`_ on ``ST_distance_sphere``.
+.. [#fndistsphere15] *See* `PostGIS 1.5 documentation <http://postgis.refractions.net/documentation/manual-1.5/ST_Distance_Sphere.html>`_ on ``ST_distance_sphere``.
+.. [#fnmysqlidx] *See* `Creating Spatial Indexes <http://dev.mysql.com/doc/refman/5.1/en/creating-spatial-indexes.html>`_
+ in the MySQL 5.1 Reference Manual:
+
+ For MyISAM tables, ``SPATIAL INDEX`` creates an R-tree index. For storage
+ engines that support nonspatial indexing of spatial columns, the engine
+ creates a B-tree index. A B-tree index on spatial values will be useful
+ for exact-value lookups, but not for range scans.
+
+.. [#] Refer :ref:`mysql-spatial-limitations` section for more details.
diff --git a/parts/django/docs/ref/contrib/gis/deployment.txt b/parts/django/docs/ref/contrib/gis/deployment.txt
new file mode 100644
index 0000000..035b23f
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/deployment.txt
@@ -0,0 +1,99 @@
+===================
+Deploying GeoDjango
+===================
+
+.. warning::
+
+ GeoDjango uses the GDAL geospatial library which is
+ not thread safe at this time. Thus, it is *highly* recommended
+ to not use threading when deploying -- in other words, use a
+ an appropriate configuration of Apache or the prefork method
+ when using FastCGI through another Web server.
+
+Apache
+======
+In this section there are some example ``VirtualHost`` directives for
+when deploying using either ``mod_python`` or ``mod_wsgi``. At this
+time, we recommend ``mod_wsgi``, as it is now officially recommended
+way to deploy Django applications with Apache. Moreover, if
+``mod_python`` is used, then a prefork version of Apache must also be
+used. As long as ``mod_wsgi`` is configured correctly, it does not
+matter whether the version of Apache is prefork or worker.
+
+.. note::
+
+ The ``Alias`` and ``Directory`` configurations in the the examples
+ below use an example path to a system-wide installation folder of Django.
+ Substitute in an appropriate location, if necessary, as it may be
+ different than the path on your system.
+
+``mod_wsgi``
+------------
+
+Example::
+
+ <VirtualHost *:80>
+ WSGIDaemonProcess geodjango user=geo group=geo processes=5 threads=1
+ WSGIProcessGroup geodjango
+ WSGIScriptAlias / /home/geo/geodjango/world.wsgi
+
+ Alias /media/ "/usr/lib/python2.5/site-packages/django/contrib/admin/media/"
+ <Directory "/usr/lib/python2.5/site-packages/django/contrib/admin/media/">
+ Order allow,deny
+ Options Indexes
+ Allow from all
+ IndexOptions FancyIndexing
+ </Directory>
+
+ </VirtualHost>
+
+.. warning::
+
+ If the ``WSGIDaemonProcess`` attribute ``threads`` is not set to ``1``, then
+ Apache may crash when running your GeoDjango application. Increase the
+ number of ``processes`` instead.
+
+For more information, please consult Django's
+:doc:`mod_wsgi documentation </howto/deployment/modwsgi>`.
+
+``mod_python``
+--------------
+
+Example::
+
+ <VirtualHost *:80>
+
+ <Location "/">
+ SetHandler mod_python
+ PythonHandler django.core.handlers.modpython
+ SetEnv DJANGO_SETTINGS_MODULE world.settings
+ PythonDebug On
+ PythonPath "['/var/www/apps'] + sys.path"
+ </Location>
+
+ Alias /media/ "/usr/lib/python2.5/site-packages/django/contrib/admin/media/"
+ <Location "/media">
+ SetHandler None
+ </Location>
+
+ </VirtualHost>
+
+.. warning::
+
+ When using ``mod_python`` you *must* be using a prefork version of Apache, or
+ else your GeoDjango application may crash Apache.
+
+For more information, please consult Django's
+:doc:`mod_python documentation </howto/deployment/modpython>`.
+
+Lighttpd
+========
+
+FastCGI
+-------
+
+Nginx
+=====
+
+FastCGI
+-------
diff --git a/parts/django/docs/ref/contrib/gis/feeds.txt b/parts/django/docs/ref/contrib/gis/feeds.txt
new file mode 100644
index 0000000..7c3a2d0
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/feeds.txt
@@ -0,0 +1,95 @@
+================
+Geographic Feeds
+================
+
+.. module:: django.contrib.gis.feeds
+ :synopsis: GeoDjango's framework for generating spatial feeds.
+
+GeoDjango has its own :class:`Feed` subclass that may embed location information
+in RSS/Atom feeds formatted according to either the `Simple GeoRSS`__ or
+`W3C Geo`_ standards. Because GeoDjango's syndication API is a superset of
+Django's, please consult :doc:`Django's syndication documentation
+</ref/contrib/syndication>` for details on general usage.
+
+.. _W3C Geo: http://www.w3.org/2003/01/geo/
+
+__ http://georss.org/1.0#simple
+
+Example
+=======
+
+API Reference
+=============
+
+``Feed`` Subclass
+-----------------
+
+.. class:: Feed
+
+ In addition to methods provided by
+ the :class:`django.contrib.syndication.feeds.Feed`
+ base class, GeoDjango's ``Feed`` class provides
+ the following overrides. Note that these overrides may be done in multiple ways::
+
+ from django.contrib.gis.feeds import Feed
+
+ class MyFeed(Feed):
+
+ # First, as a class attribute.
+ geometry = ...
+ item_geometry = ...
+
+ # Also a function with no arguments
+ def geometry(self):
+ ...
+
+ def item_geometry(self):
+ ...
+
+ # And as a function with a single argument
+ def geometry(self, obj):
+ ...
+
+ def item_geometry(self, item):
+ ...
+
+ .. method:: geometry(obj)
+
+ Takes the object returned by ``get_object()`` and returns the *feed's*
+ geometry. Typically this is a ``GEOSGeometry`` instance, or can be a
+ tuple to represent a point or a box. For example::
+
+ class ZipcodeFeed(Feed):
+
+ def geometry(self, obj):
+ # Can also return: `obj.poly`, and `obj.poly.centroid`.
+ return obj.poly.extent # tuple like: (X0, Y0, X1, Y1).
+
+ .. method:: item_geometry(item)
+
+ Set this to return the geometry for each *item* in the feed. This
+ can be a ``GEOSGeometry`` instance, or a tuple that represents a
+ point coordinate or bounding box. For example::
+
+ class ZipcodeFeed(Feed):
+
+ def item_geometry(self, obj):
+ # Returns the polygon.
+ return obj.poly
+
+``SyndicationFeed`` Subclasses
+------------------------------
+
+The following :class:`django.utils.feedgenerator.SyndicationFeed` subclasses
+are available:
+
+.. class:: GeoRSSFeed
+
+.. class:: GeoAtom1Feed
+
+.. class:: W3CGeoFeed
+
+.. note::
+
+ `W3C Geo`_ formatted feeds only support
+ :class:`~django.contrib.gis.db.models.PointField` geometries.
diff --git a/parts/django/docs/ref/contrib/gis/gdal.txt b/parts/django/docs/ref/contrib/gis/gdal.txt
new file mode 100644
index 0000000..1ce21d9
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/gdal.txt
@@ -0,0 +1,1114 @@
+.. _ref-gdal:
+
+========
+GDAL API
+========
+
+.. module:: django.contrib.gis.gdal
+ :synopsis: GeoDjango's high-level interface to the GDAL library.
+
+`GDAL`__ stands for **G**\ eospatial **D**\ ata **A**\ bstraction **L**\ ibrary,
+and is a veritable "swiss army knife" of GIS data functionality. A subset
+of GDAL is the `OGR`__ Simple Features Library, which specializes
+in reading and writing vector geographic data in a variety of standard
+formats.
+
+GeoDjango provides a high-level Python interface for some of the
+capabilities of OGR, including the reading and coordinate transformation
+of vector spatial data.
+
+.. note::
+
+ Although the module is named ``gdal``, GeoDjango only supports
+ some of the capabilities of OGR. Thus, none of GDAL's features
+ with respect to raster (image) data are supported at this time.
+
+__ http://www.gdal.org/
+__ http://www.gdal.org/ogr/
+
+Overview
+========
+
+Sample Data
+-----------
+
+The GDAL/OGR tools described here are designed to help you read in
+your geospatial data, in order for most of them to be useful you have
+to have some data to work with. If you're starting out and don't yet
+have any data of your own to use, GeoDjango comes with a number of
+simple data sets that you can use for testing. This snippet will
+determine where these sample files are installed on your computer::
+
+ >>> import os
+ >>> import django.contrib.gis
+ >>> GIS_PATH = os.path.dirname(django.contrib.gis.__file__)
+ >>> CITIES_PATH = os.path.join(GIS_PATH, 'tests/data/cities/cities.shp')
+
+Vector Data Source Objects
+==========================
+
+``DataSource``
+--------------
+
+:class:`DataSource` is a wrapper for the OGR data source object that
+supports reading data from a variety of OGR-supported geospatial file
+formats and data sources using a simple, consistent interface. Each
+data source is represented by a :class:`DataSource` object which contains
+one or more layers of data. Each layer, represented by a :class:`Layer`
+object, contains some number of geographic features (:class:`Feature`),
+information about the type of features contained in that layer (e.g.
+points, polygons, etc.), as well as the names and types of any
+additional fields (:class:`Field`) of data that may be associated with
+each feature in that layer.
+
+.. class:: DataSource(ds_input)
+
+ The constructor for ``DataSource`` just a single parameter: the path of
+ the file you want to read. However, OGR
+ also supports a variety of more complex data sources, including
+ databases, that may be accessed by passing a special name string instead
+ of a path. For more information, see the `OGR Vector Formats`__
+ documentation. The :attr:`name` property of a ``DataSource``
+ instance gives the OGR name of the underlying data source that it is
+ using.
+
+ Once you've created your ``DataSource``, you can find out how many
+ layers of data it contains by accessing the :attr:`layer_count` property,
+ or (equivalently) by using the ``len()`` function. For information on
+ accessing the layers of data themselves, see the next section::
+
+ >>> from django.contrib.gis.gdal import DataSource
+ >>> ds = DataSource(CITIES_PATH)
+ >>> ds.name # The exact filename may be different on your computer
+ '/usr/local/lib/python2.6/site-packages/django/contrib/gis/tests/data/cities/cities.shp'
+ >>> ds.layer_count # This file only contains one layer
+ 1
+
+ .. attribute:: layer_count
+
+ Returns the number of layers in the data source.
+
+ .. attribute:: name
+
+ Returns the name of the data source.
+
+__ http://www.gdal.org/ogr/ogr_formats.html
+
+``Layer``
+---------
+
+.. class:: Layer
+
+ ``Layer`` is a wrapper for a layer of data in a ``DataSource`` object.
+ You never create a ``Layer`` object directly. Instead, you retrieve
+ them from a :class:`DataSource` object, which is essentially a standard
+ Python container of ``Layer`` objects. For example, you can access a
+ specific layer by its index (e.g. ``ds[0]`` to access the first
+ layer), or you can iterate over all the layers in the container in a
+ ``for`` loop. The ``Layer`` itself acts as a container for geometric
+ features.
+
+ Typically, all the features in a given layer have the same geometry type.
+ The :attr:`geom_type` property of a layer is an :class:`OGRGeomType`
+ that identifies the feature type. We can use it to print out some basic
+ information about each layer in a :class:`DataSource`::
+
+ >>> for layer in ds:
+ ... print 'Layer "%s": %i %ss' % (layer.name, len(layer), layer.geom_type.name)
+ ...
+ Layer "cities": 3 Points
+
+ The example output is from the cities data source, loaded above, which
+ evidently contains one layer, called ``"cities"``, which contains three
+ point features. For simplicity, the examples below assume that you've
+ stored that layer in the variable ``layer``::
+
+ >>> layer = ds[0]
+
+ .. attribute:: name
+
+ Returns the name of this layer in the data source.
+
+ >>> layer.name
+ 'cities'
+
+ .. attribute:: num_feat
+
+ Returns the number of features in the layer. Same as ``len(layer)``::
+
+ >>> layer.num_feat
+ 3
+
+ .. attribute:: geom_type
+
+ Returns the geometry type of the layer, as an :class:`OGRGeomType`
+ object::
+
+ >>> layer.geom_type.name
+ 'Point'
+
+ .. attribute:: num_fields
+
+ Returns the number of fields in the layer, i.e the number of fields of
+ data associated with each feature in the layer::
+
+ >>> layer.num_fields
+ 4
+
+ .. attribute:: fields
+
+ Returns a list of the names of each of the fields in this layer::
+
+ >>> layer.fields
+ ['Name', 'Population', 'Density', 'Created']
+
+ .. attribute field_types
+
+ Returns a list of the data types of each of the fields in this layer.
+ These are subclasses of ``Field``, discussed below::
+
+ >>> [ft.__name__ for ft in layer.field_types]
+ ['OFTString', 'OFTReal', 'OFTReal', 'OFTDate']
+
+ .. attribute:: field_widths
+
+ Returns a list of the maximum field widths for each of the fields in
+ this layer::
+
+ >>> layer.field_widths
+ [80, 11, 24, 10]
+
+ .. attribute:: field_precisions
+
+ Returns a list of the numeric precisions for each of the fields in
+ this layer. This is meaningless (and set to zero) for non-numeric
+ fields::
+
+ >>> layer.field_precisions
+ [0, 0, 15, 0]
+
+ .. attribute:: extent
+
+ Returns the spatial extent of this layer, as an :class:`Envelope`
+ object::
+
+ >>> layer.extent.tuple
+ (-104.609252, 29.763374, -95.23506, 38.971823)
+
+ .. attribute:: srs
+
+ Property that returns the :class:`SpatialReference` associated
+ with this layer::
+
+ >>> print layer.srs
+ GEOGCS["GCS_WGS_1984",
+ DATUM["WGS_1984",
+ SPHEROID["WGS_1984",6378137,298.257223563]],
+ PRIMEM["Greenwich",0],
+ UNIT["Degree",0.017453292519943295]]
+
+ If the :class:`Layer` has no spatial reference information associated
+ with it, ``None`` is returned.
+
+ .. attribute:: spatial_filter
+
+ .. versionadded:: 1.2
+
+ Property that may be used to retrieve or set a spatial filter for this
+ layer. A spatial filter can only be set with an :class:`OGRGeometry`
+ instance, a 4-tuple extent, or ``None``. When set with something
+ other than ``None``, only features that intersect the filter will be
+ returned when iterating over the layer::
+
+ >>> print layer.spatial_filter
+ None
+ >>> print len(layer)
+ 3
+ >>> [feat.get('Name') for feat in layer]
+ ['Pueblo', 'Lawrence', 'Houston']
+ >>> ks_extent = (-102.051, 36.99, -94.59, 40.00) # Extent for state of Kansas
+ >>> layer.spatial_filter = ks_extent
+ >>> len(layer)
+ 1
+ >>> [feat.get('Name') for feat in layer]
+ ['Lawrence']
+ >>> layer.spatial_filter = None
+ >>> len(layer)
+ 3
+
+ .. method:: get_fields()
+
+ A method that returns a list of the values of a given field for each
+ feature in the layer::
+
+ >>> layer.get_fields('Name')
+ ['Pueblo', 'Lawrence', 'Houston']
+
+ .. method:: get_geoms([geos=False])
+
+ A method that returns a list containing the geometry of each feature
+ in the layer. If the optional argument ``geos`` is set to ``True``
+ then the geometries are converted to :class:`~django.contrib.gis.geos.GEOSGeometry`
+ objects. Otherwise, they are returned as :class:`OGRGeometry` objects::
+
+ >>> [pt.tuple for pt in layer.get_geoms()]
+ [(-104.609252, 38.255001), (-95.23506, 38.971823), (-95.363151, 29.763374)]
+
+ .. method:: test_capability(capability)
+
+ Returns a boolean indicating whether this layer supports the
+ given capability (a string). Examples of valid capability strings
+ include: ``'RandomRead'``, ``'SequentialWrite'``, ``'RandomWrite'``,
+ ``'FastSpatialFilter'``, ``'FastFeatureCount'``, ``'FastGetExtent'``,
+ ``'CreateField'``, ``'Transactions'``, ``'DeleteFeature'``, and
+ ``'FastSetNextByIndex'``.
+
+``Feature``
+-----------
+
+.. class:: Feature
+
+
+ ``Feature`` wraps an OGR feature. You never create a ``Feature``
+ object directly. Instead, you retrieve them from a :class:`Layer` object.
+ Each feature consists of a geometry and a set of fields containing
+ additional properties. The geometry of a field is accessible via its
+ ``geom`` property, which returns an :class:`OGRGeometry` object. A ``Feature``
+ behaves like a standard Python container for its fields, which it returns as
+ :class:`Field` objects: you can access a field directly by its index or name,
+ or you can iterate over a feature's fields, e.g. in a ``for`` loop.
+
+ .. attribute:: geom
+
+ Returns the geometry for this feature, as an ``OGRGeometry`` object::
+
+ >>> city.geom.tuple
+ (-104.609252, 38.255001)
+
+ .. attribute:: get
+
+ A method that returns the value of the given field (specified by name)
+ for this feature, **not** a ``Field`` wrapper object::
+
+ >>> city.get('Population')
+ 102121
+
+ .. attribute:: geom_type
+
+ Returns the type of geometry for this feature, as an :class:`OGRGeomType`
+ object. This will be the same for all features in a given layer, and
+ is equivalent to the :attr:`Layer.geom_type` property of the
+ :class:`Layer`` object the feature came from.
+
+ .. attribute:: num_fields
+
+ Returns the number of fields of data associated with the feature.
+ This will be the same for all features in a given layer, and is
+ equivalent to the :attr:`Layer.num_fields` property of the
+ :class:`Layer` object the feature came from.
+
+ .. attribute:: fields
+
+ Returns a list of the names of the fields of data associated with the
+ feature. This will be the same for all features in a given layer, and
+ is equivalent to the :attr:`Layer.fields` property of the :class:`Layer`
+ object the feature came from.
+
+ .. attribute:: fid
+
+ Returns the feature identifier within the layer::
+
+ >>> city.fid
+ 0
+
+ .. attribute:: layer_name
+
+ Returns the name of the :class:`Layer` that the feature came from.
+ This will be the same for all features in a given layer::
+
+ >>> city.layer_name
+ 'cities'
+
+ .. attribute:: index
+
+ A method that returns the index of the given field name. This will be
+ the same for all features in a given layer::
+
+ >>> city.index('Population')
+ 1
+
+``Field``
+---------
+
+.. class:: Field
+
+ .. attribute:: name
+
+ Returns the name of this field::
+
+ >>> city['Name'].name
+ 'Name'
+
+ .. attribute:: type
+
+ Returns the OGR type of this field, as an integer. The
+ ``FIELD_CLASSES`` dictionary maps these values onto
+ subclasses of ``Field``::
+
+ >>> city['Density'].type
+ 2
+
+ .. attribute:: type_name
+
+ Returns a string with the name of the data type of this field::
+
+ >>> city['Name'].type_name
+ 'String'
+
+ .. attribute:: value
+
+ Returns the value of this field. The ``Field`` class itself
+ returns the value as a string, but each subclass returns the
+ value in the most appropriate form::
+
+ >>> city['Population'].value
+ 102121
+
+ .. attribute:: width
+
+ Returns the width of this field::
+
+ >>> city['Name'].width
+ 80
+
+ .. attribute:: precision
+
+ Returns the numeric precision of this field. This is meaningless (and
+ set to zero) for non-numeric fields::
+
+ >>> city['Density'].precision
+ 15
+
+ .. method:: as_double()
+
+ Returns the value of the field as a double (float)::
+
+ >>> city['Density'].as_double()
+ 874.7
+
+ .. method:: as_int()
+
+ Returns the value of the field as an integer::
+
+ >>> city['Population'].as_int()
+ 102121
+
+ .. method:: as_string()
+
+ Returns the value of the field as a string::
+
+ >>> city['Name'].as_string()
+ 'Pueblo'
+
+ .. method:: as_datetime()
+
+ Returns the value of the field as a tuple of date and time components::
+
+ >>> city['Created'].as_datetime()
+ (c_long(1999), c_long(5), c_long(23), c_long(0), c_long(0), c_long(0), c_long(0))
+
+``Driver``
+----------
+
+.. class:: Driver(dr_input)
+
+ The ``Driver`` class is used internally to wrap an OGR :class:`DataSource` driver.
+
+ .. attribute:: driver_count
+
+ Returns the number of OGR vector drivers currently registered.
+
+
+OGR Geometries
+==============
+
+``OGRGeometry``
+---------------
+
+:class:`OGRGeometry` objects share similar functionality with
+:class:`~django.contrib.gis.geos.GEOSGeometry` objects, and are thin
+wrappers around OGR's internal geometry representation. Thus,
+they allow for more efficient access to data when using :class:`DataSource`.
+Unlike its GEOS counterpart, :class:`OGRGeometry` supports spatial reference
+systems and coordinate transformation::
+
+ >>> from django.contrib.gis.gdal import OGRGeometry
+ >>> polygon = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5))')
+
+.. class:: OGRGeometry(geom_input[, srs=None])
+
+ This object is a wrapper for the `OGR Geometry`__ class.
+ These objects are instantiated directly from the given ``geom_input``
+ parameter, which may be a string containing WKT or HEX, a ``buffer``
+ containing WKB data, or an :class:`OGRGeomType` object. These objects
+ are also returned from the :class:`Feature.geom` attribute, when
+ reading vector data from :class:`Layer` (which is in turn a part of
+ a :class:`DataSource`).
+
+ __ http://www.gdal.org/ogr/classOGRGeometry.html
+
+ .. classmethod:: from_bbox(bbox)
+
+ .. versionadded:: 1.1
+
+ Constructs a :class:`Polygon` from the given bounding-box (a 4-tuple).
+
+ .. method:: __len__
+
+ Returns the number of points in a :class:`LineString`, the
+ number of rings in a :class:`Polygon`, or the number of geometries in a
+ :class:`GeometryCollection`. Not applicable to other geometry types.
+
+ .. method:: __iter__
+
+ Iterates over the points in a :class:`LineString`, the rings in a
+ :class:`Polygon`, or the geometries in a :class:`GeometryCollection`.
+ Not applicable to other geometry types.
+
+ .. method:: __getitem__
+
+ Returns the point at the specified index for a :class:`LineString`, the
+ interior ring at the specified index for a :class:`Polygon`, or the geometry
+ at the specified index in a :class:`GeometryCollection`. Not applicable to
+ other geometry types.
+
+ .. attribute:: dimension
+
+ Returns the number of coordinated dimensions of the geometry, i.e. 0
+ for points, 1 for lines, and so forth::
+
+ >> polygon.dimension
+ 2
+
+ .. attribute:: coord_dim
+
+ .. versionchanged:: 1.2
+
+ Returns or sets the coordinate dimension of this geometry. For
+ example, the value would be 2 for two-dimensional geometries.
+
+ .. note::
+
+ Setting this property is only available in versions 1.2 and above.
+
+ .. attribute:: geom_count
+
+ Returns the number of elements in this geometry::
+
+ >>> polygon.geom_count
+ 1
+
+ .. attribute:: point_count
+
+ Returns the number of points used to describe this geometry::
+
+ >>> polygon.point_count
+ 4
+
+ .. attribute:: num_points
+
+ Alias for :attr:`point_count`.
+
+ .. attribute:: num_coords
+
+ Alias for :attr:`point_count`.
+
+ .. attribute:: geom_type
+
+ Returns the type of this geometry, as an :class:`OGRGeomType` object.
+
+ .. attribute:: geom_name
+
+ Returns the name of the type of this geometry::
+
+ >>> polygon.geom_name
+ 'POLYGON'
+
+ .. attribute:: area
+
+ Returns the area of this geometry, or 0 for geometries that do not
+ contain an area::
+
+ >>> polygon.area
+ 25.0
+
+ .. attribute:: envelope
+
+ Returns the envelope of this geometry, as an :class:`Envelope` object.
+
+ .. attribute:: extent
+
+ Returns the envelope of this geometry as a 4-tuple, instead of as an
+ :class:`Envelope` object::
+
+ >>> point.extent
+ (0.0, 0.0, 5.0, 5.0)
+
+ .. attribute:: srs
+
+ This property controls the spatial reference for this geometry, or
+ ``None`` if no spatial reference system has been assigned to it.
+ If assigned, accessing this property returns a :class:`SpatialReference`
+ object. It may be set with another :class:`SpatialReference` object,
+ or any input that :class:`SpatialReference` accepts. Example::
+
+ >>> city.geom.srs.name
+ 'GCS_WGS_1984'
+
+ .. attribute:: srid
+
+ Returns or sets the spatial reference identifier corresponding to
+ :class:`SpatialReference` of this geometry. Returns ``None`` if
+ there is no spatial reference information associated with this
+ geometry, or if an SRID cannot be determined.
+
+ .. attribute:: geos
+
+ Returns a :class:`~django.contrib.gis.geos.GEOSGeometry` object
+ corresponding to this geometry.
+
+ .. attribute:: gml
+
+ Returns a string representation of this geometry in GML format::
+
+ >>> OGRGeometry('POINT(1 2)').gml
+ '<gml:Point><gml:coordinates>1,2</gml:coordinates></gml:Point>'
+
+ .. attribute:: hex
+
+ Returns a string representation of this geometry in HEX WKB format::
+
+ >>> OGRGeometry('POINT(1 2)').hex
+ '0101000000000000000000F03F0000000000000040'
+
+ .. attribute:: json
+
+ Returns a string representation of this geometry in JSON format::
+
+ >>> OGRGeometry('POINT(1 2)').json
+ '{ "type": "Point", "coordinates": [ 1.000000, 2.000000 ] }'
+
+
+ .. attribute:: kml
+
+ .. versionadded:: 1.1
+
+ Returns a string representation of this geometry in KML format.
+
+ .. attribute:: wkb_size
+
+ Returns the size of the WKB buffer needed to hold a WKB representation
+ of this geometry::
+
+ >>> OGRGeometry('POINT(1 2)').wkb_size
+ 21
+
+ .. attribute:: wkb
+
+ Returns a ``buffer`` containing a WKB representation of this geometry.
+
+ .. attribute:: wkt
+
+ Returns a string representation of this geometry in WKT format.
+
+ .. attribute:: ewkt
+
+ .. versionadded:: 1.2
+
+ Returns the EWKT representation of this geometry.
+
+ .. method:: clone()
+
+ Returns a new :class:`OGRGeometry` clone of this geometry object.
+
+ .. method:: close_rings()
+
+ If there are any rings within this geometry that have not been closed,
+ this routine will do so by adding the starting point to the end::
+
+ >>> triangle = OGRGeometry('LINEARRING (0 0,0 1,1 0)')
+ >>> triangle.close_rings()
+ >>> triangle.wkt
+ 'LINEARRING (0 0,0 1,1 0,0 0)'
+
+ .. method:: transform(coord_trans, clone=False)
+
+ Transforms this geometry to a different spatial reference system. May
+ take a :class:`CoordTransform` object, a :class:`SpatialReference` object,
+ or any other input accepted by :class:`SpatialReference` (including
+ spatial reference WKT and PROJ.4 strings, or an integer SRID).
+ By default nothing is returned and the geometry is transformed in-place.
+ However, if the `clone` keyword is set to ``True`` then a transformed clone
+ of this geometry is returned instead.
+
+ .. method:: intersects(other)
+
+ Returns ``True`` if this geometry intersects the other, otherwise returns
+ ``False``.
+
+ .. method:: equals(other)
+
+ Returns ``True`` if this geometry is equivalent to the other, otherwise returns
+ ``False``.
+
+ .. method:: disjoint(other)
+
+ Returns ``True`` if this geometry is spatially disjoint to (i.e. does
+ not intersect) the other, otherwise returns ``False``.
+
+ .. method:: touches(other)
+
+ Returns ``True`` if this geometry touches the other, otherwise returns
+ ``False``.
+
+ .. method:: crosses(other)
+
+ Returns ``True`` if this geometry crosses the other, otherwise returns
+ ``False``.
+
+ .. method:: within(other)
+
+ Returns ``True`` if this geometry is contained within the other, otherwise returns
+ ``False``.
+
+ .. method:: contains(other)
+
+ Returns ``True`` if this geometry contains the other, otherwise returns
+ ``False``.
+
+ .. method:: overlaps(other)
+
+ Returns ``True`` if this geometry overlaps the other, otherwise returns
+ ``False``.
+
+ .. method:: boundary
+
+ The boundary of this geometry, as a new :class:`OGRGeometry` object.
+
+ .. attribute:: convex_hull
+
+ The smallest convex polygon that contains this geometry, as a new
+ :class:`OGRGeometry` object.
+
+ .. method:: difference
+
+ Returns the region consisting of the difference of this geometry and
+ the other, as a new :class:`OGRGeometry` object.
+
+ .. method:: intersection
+
+ Returns the region consisting of the intersection of this geometry and
+ the other, as a new :class:`OGRGeometry` object.
+
+ .. method:: sym_difference
+
+ Returns the region consisting of the symmetric difference of this
+ geometry and the other, as a new :class:`OGRGeometry` object.
+
+ .. method:: union
+
+ Returns the region consisting of the union of this geometry and
+ the other, as a new :class:`OGRGeometry` object.
+
+ .. attribute:: tuple
+
+ Returns the coordinates of a point geometry as a tuple, the
+ coordinates of a line geometry as a tuple of tuples, and so forth::
+
+ >>> OGRGeometry('POINT (1 2)').tuple
+ (1.0, 2.0)
+ >>> OGRGeometry('LINESTRING (1 2,3 4)').tuple
+ ((1.0, 2.0), (3.0, 4.0))
+
+ .. attribute:: coords
+
+ An alias for :attr:`tuple`.
+
+.. class:: Point
+
+ .. attribute:: x
+
+ Returns the X coordinate of this point::
+
+ >>> OGRGeometry('POINT (1 2)').x
+ 1.0
+
+ .. attribute:: y
+
+ Returns the Y coordinate of this point::
+
+ >>> OGRGeometry('POINT (1 2)').y
+ 2.0
+
+ .. attribute:: z
+
+ Returns the Z coordinate of this point, or ``None`` if the
+ the point does not have a Z coordinate::
+
+ >>> OGRGeometry('POINT (1 2 3)').z
+ 3.0
+
+.. class:: LineString
+
+ .. attribute:: x
+
+ Returns a list of X coordinates in this line::
+
+ >>> OGRGeometry('LINESTRING (1 2,3 4)').x
+ [1.0, 3.0]
+
+ .. attribute:: y
+
+ Returns a list of Y coordinates in this line::
+
+ >>> OGRGeometry('LINESTRING (1 2,3 4)').y
+ [2.0, 4.0]
+
+ .. attribute:: z
+
+ Returns a list of Z coordinates in this line, or ``None`` if the
+ line does not have Z coordinates::
+
+ >>> OGRGeometry('LINESTRING (1 2 3,4 5 6)').z
+ [3.0, 6.0]
+
+
+.. class:: Polygon
+
+ .. attribute:: shell
+
+ Returns the shell or exterior ring of this polygon, as a ``LinearRing``
+ geometry.
+
+ .. attribute:: exterior_ring
+
+ An alias for :attr:`shell`.
+
+ .. attribute:: centroid
+
+ Returns a :class:`Point` representing the centroid of this polygon.
+
+.. class:: GeometryCollection
+
+ .. method:: add(geom)
+
+ Adds a geometry to this geometry collection. Not applicable to other
+ geometry types.
+
+
+``OGRGeomType``
+---------------
+
+.. class:: OGRGeomType(type_input)
+
+ This class allows for the representation of an OGR geometry type
+ in any of several ways::
+
+ >>> from django.contrib.gis.gdal import OGRGeomType
+ >>> gt1 = OGRGeomType(3) # Using an integer for the type
+ >>> gt2 = OGRGeomType('Polygon') # Using a string
+ >>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive
+ >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
+ True True
+
+ .. attribute:: name
+
+ Returns a short-hand string form of the OGR Geometry type::
+
+ >>> gt1.name
+ 'Polygon'
+
+ .. attribute:: num
+
+ Returns the number corresponding to the OGR geometry type::
+
+ >>> gt1.num
+ 3
+
+ .. attribute:: django
+
+ Returns the Django field type (a subclass of GeometryField) to use for
+ storing this OGR type, or ``None`` if there is no appropriate Django
+ type::
+
+ >>> gt1.django
+ 'PolygonField'
+
+``Envelope``
+------------
+
+.. class:: Envelope(*args)
+
+ Represents an OGR Envelope structure that contains the
+ minimum and maximum X, Y coordinates for a rectangle bounding box.
+ The naming of the variables is compatible with the OGR Envelope
+ C structure.
+
+ .. attribute:: min_x
+
+ The value of the minimum X coordinate.
+
+ .. attribute:: min_y
+
+ The value of the maximum X coordinate.
+
+ .. attribute:: max_x
+
+ The value of the minimum Y coordinate.
+
+ .. attribute:: max_y
+
+ The value of the maximum Y coordinate.
+
+ .. attribute:: ur
+
+ The upper-right coordinate, as a tuple.
+
+ .. attribute:: ll
+
+ The lower-left coordinate, as a tuple.
+
+ .. attribute:: tuple
+
+ A tuple representing the envelope.
+
+ .. attribute:: wkt
+
+ A string representing this envelope as a polygon in WKT format.
+
+
+ .. method:: expand_to_include(self, *args)
+
+ .. versionadded:: 1.1
+
+Coordinate System Objects
+=========================
+
+``SpatialReference``
+--------------------
+
+.. class:: SpatialReference(srs_input)
+
+ Spatial reference objects are initialized on the given ``srs_input``,
+ which may be one of the following:
+
+ * OGC Well Known Text (WKT) (a string)
+ * EPSG code (integer or string)
+ * PROJ.4 string
+ * A shorthand string for well-known standards (``'WGS84'``, ``'WGS72'``, ``'NAD27'``, ``'NAD83'``)
+
+ Example::
+
+ >>> wgs84 = SpatialReference('WGS84') # shorthand string
+ >>> wgs84 = SpatialReference(4326) # EPSG code
+ >>> wgs84 = SpatialReference('EPSG:4326') # EPSG string
+ >>> proj4 = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '
+ >>> wgs84 = SpatialReference(proj4) # PROJ.4 string
+ >>> wgs84 = SpatialReference("""GEOGCS["WGS 84",
+ DATUM["WGS_1984",
+ SPHEROID["WGS 84",6378137,298.257223563,
+ AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich",0,
+ AUTHORITY["EPSG","8901"]],
+ UNIT["degree",0.01745329251994328,
+ AUTHORITY["EPSG","9122"]],
+ AUTHORITY["EPSG","4326"]]""") # OGC WKT
+
+ .. method:: __getitem__(target)
+
+ Returns the value of the given string attribute node, ``None`` if the node
+ doesn't exist. Can also take a tuple as a parameter, (target, child),
+ where child is the index of the attribute in the WKT. For example::
+
+ >>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]')
+ >>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326
+ >>> print srs['GEOGCS']
+ WGS 84
+ >>> print srs['DATUM']
+ WGS_1984
+ >>> print srs['AUTHORITY']
+ EPSG
+ >>> print srs['AUTHORITY', 1] # The authority value
+ 4326
+ >>> print srs['TOWGS84', 4] # the fourth value in this wkt
+ 0
+ >>> print srs['UNIT|AUTHORITY'] # For the units authority, have to use the pipe symbole.
+ EPSG
+ >>> print srs['UNIT|AUTHORITY', 1] # The authority value for the untis
+ 9122
+
+ .. method:: attr_value(target, index=0)
+
+ The attribute value for the given target node (e.g. ``'PROJCS'``).
+ The index keyword specifies an index of the child node to return.
+
+ .. method:: auth_name(target)
+
+ Returns the authority name for the given string target node.
+
+ .. method:: auth_code(target)
+
+ Returns the authority code for the given string target node.
+
+ .. method:: clone()
+
+ Returns a clone of this spatial reference object.
+
+ .. method:: identify_epsg()
+
+ This method inspects the WKT of this SpatialReference, and will
+ add EPSG authority nodes where an EPSG identifier is applicable.
+
+ .. method:: from_esri()
+
+ Morphs this SpatialReference from ESRI's format to EPSG
+
+ .. method:: to_esri()
+
+ Morphs this SpatialReference to ESRI's format.
+
+ .. method:: validate()
+
+ Checks to see if the given spatial reference is valid, if not
+ an exception will be raised.
+
+ .. method:: import_epsg(epsg)
+
+ Import spatial reference from EPSG code.
+
+ .. method:: import_proj(proj)
+
+ Import spatial reference from PROJ.4 string.
+
+ .. method:: import_user_input(user_input)
+
+ .. versionadded:: 1.1
+
+ .. method:: import_wkt(wkt)
+
+ Import spatial reference from WKT.
+
+ .. method:: import_xml(xml)
+
+ Import spatial reference from XML.
+
+ .. attribute:: name
+
+ Returns the name of this Spatial Reference.
+
+ .. attribute:: srid
+
+ Returns the SRID of top-level authority, or ``None`` if undefined.
+
+ .. attribute:: linear_name
+
+ Returns the name of the linear units.
+
+ .. attribute:: linear_units
+
+ Returns the value of the linear units.
+
+ .. attribute:: angular_name
+
+ Returns the name of the angular units."
+
+ .. attribute:: angular_units
+
+ Returns the value of the angular units.
+
+ .. attribute:: units
+
+ Returns a 2-tuple of the units value and the units name,
+ and will automatically determines whether to return the linear
+ or angular units.
+
+ .. attribute:: ellisoid
+
+ Returns a tuple of the ellipsoid parameters for this spatial
+ reference: (semimajor axis, semiminor axis, and inverse flattening)
+
+ .. attribute:: semi_major
+
+ Returns the semi major axis of the ellipsoid for this spatial reference.
+
+ .. attribute:: semi_minor
+
+ Returns the semi minor axis of the ellipsoid for this spatial reference.
+
+ .. attribute:: inverse_flattening
+
+ Returns the inverse flattening of the ellipsoid for this spatial reference.
+
+ .. attribute:: geographic
+
+ Returns ``True`` if this spatial reference is geographic
+ (root node is ``GEOGCS``).
+
+ .. attribute:: local
+
+ Returns ``True`` if this spatial reference is local
+ (root node is ``LOCAL_CS``).
+
+ .. attribute:: projected
+
+ Returns ``True`` if this spatial reference is a projected coordinate
+ system (root node is ``PROJCS``).
+
+ .. attribute:: wkt
+
+ Returns the WKT representation of this spatial reference.
+
+ .. attribute:: pretty_wkt
+
+ Returns the 'pretty' representation of the WKT.
+
+ .. attribute:: proj
+
+ Returns the PROJ.4 representation for this spatial reference.
+
+ .. attribute:: proj4
+
+ Alias for :attr:`SpatialReference.proj`.
+
+ .. attribute:: xml
+
+ Returns the XML representation of this spatial reference.
+
+
+``CoordTransform``
+------------------
+
+.. class:: CoordTransform(source, target)
+
+Represents a coordinate system transform. It is initialized with two
+:class:`SpatialReference`, representing the source and target coordinate
+systems, respectively. These objects should be used when performing
+the same coordinate transformation repeatedly on different geometries::
+
+ >>> ct = CoordTransform(SpatialReference('WGS84'), SpatialReference('NAD83'))
+ >>> for feat in layer:
+ ... geom = feat.geom # getting clone of feature geometry
+ ... geom.transform(ct) # transforming
+
+Settings
+========
+
+.. setting:: GDAL_LIBRARY_PATH
+
+GDAL_LIBRARY_PATH
+-----------------
+
+A string specifying the location of the GDAL library. Typically,
+this setting is only used if the GDAL library is in a non-standard
+location (e.g., ``/home/john/lib/libgdal.so``).
diff --git a/parts/django/docs/ref/contrib/gis/geoip.txt b/parts/django/docs/ref/contrib/gis/geoip.txt
new file mode 100644
index 0000000..784d69e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/geoip.txt
@@ -0,0 +1,223 @@
+.. _ref-geoip:
+
+======================
+Geolocation with GeoIP
+======================
+
+.. module:: django.contrib.gis.utils.geoip
+ :synopsis: High-level Python interface for MaxMind's GeoIP C library.
+
+.. currentmodule:: django.contrib.gis.utils
+
+The :class:`GeoIP` object is a ctypes wrapper for the
+`MaxMind GeoIP C API`__. [#]_ This interface is a BSD-licensed alternative
+to the GPL-licensed `Python GeoIP`__ interface provided by MaxMind.
+
+In order to perform IP-based geolocation, the :class:`GeoIP` object requires
+the GeoIP C libary and either the GeoIP `Country`__ or `City`__
+datasets in binary format (the CSV files will not work!). These datasets may be
+`downloaded from MaxMind`__. Grab the ``GeoIP.dat.gz`` and ``GeoLiteCity.dat.gz``
+and unzip them in a directory corresponding to what you set
+``GEOIP_PATH`` with in your settings. See the example and reference below
+for more details.
+
+__ http://www.maxmind.com/app/c
+__ http://www.maxmind.com/app/python
+__ http://www.maxmind.com/app/country
+__ http://www.maxmind.com/app/city
+__ http://www.maxmind.com/download/geoip/database/
+
+Example
+=======
+
+Assuming you have the GeoIP C library installed, here is an example of its
+usage::
+
+ >>> from django.contrib.gis.utils import GeoIP
+ >>> g = GeoIP()
+ >>> g.country('google.com')
+ {'country_code': 'US', 'country_name': 'United States'}
+ >>> g.city('72.14.207.99')
+ {'area_code': 650,
+ 'city': 'Mountain View',
+ 'country_code': 'US',
+ 'country_code3': 'USA',
+ 'country_name': 'United States',
+ 'dma_code': 807,
+ 'latitude': 37.419200897216797,
+ 'longitude': -122.05740356445312,
+ 'postal_code': '94043',
+ 'region': 'CA'}
+ >>> g.lat_lon('salon.com')
+ (37.789798736572266, -122.39420318603516)
+ >>> g.lon_lat('uh.edu')
+ (-95.415199279785156, 29.77549934387207)
+ >>> g.geos('24.124.1.80').wkt
+ 'POINT (-95.2087020874023438 39.0392990112304688)'
+
+``GeoIP`` Settings
+==================
+
+.. setting:: GEOIP_PATH
+
+GEOIP_PATH
+----------
+
+A string specifying the directory where the GeoIP data files are
+located. This setting is *required* unless manually specified
+with ``path`` keyword when initializing the :class:`GeoIP` object.
+
+.. setting:: GEOIP_LIBRARY_PATH
+
+GEOIP_LIBRARY_PATH
+------------------
+
+A string specifying the location of the GeoIP C library. Typically,
+this setting is only used if the GeoIP C library is in a non-standard
+location (e.g., ``/home/sue/lib/libGeoIP.so``).
+
+.. setting:: GEOIP_COUNTRY
+
+GEOIP_COUNTRY
+-------------
+
+The basename to use for the GeoIP country data file.
+Defaults to ``'GeoIP.dat'``.
+
+.. setting:: GEOIP_CITY
+
+GEOIP_CITY
+----------
+
+The basename to use for the GeoIP city data file.
+Defaults to ``'GeoLiteCity.dat'``.
+
+``GeoIP`` API
+=============
+
+.. class:: GeoIP([path=None, cache=0, country=None, city=None])
+
+The ``GeoIP`` object does not require any parameters to use the default
+settings. However, at the very least the :setting:`GEOIP_PATH` setting
+should be set with the path of the location of your GeoIP data sets. The
+following intialization keywords may be used to customize any of the
+defaults.
+
+=================== =======================================================
+Keyword Arguments Description
+=================== =======================================================
+``path`` Base directory to where GeoIP data is located or the
+ full path to where the city or country data files
+ (.dat) are located. Assumes that both the city and
+ country data sets are located in this directory;
+ overrides the :setting:`GEOIP_PATH` settings attribute.
+
+``cache`` The cache settings when opening up the GeoIP datasets,
+ and may be an integer in (0, 1, 2, 4) corresponding to
+ the ``GEOIP_STANDARD``, ``GEOIP_MEMORY_CACHE``,
+ ``GEOIP_CHECK_CACHE``, and ``GEOIP_INDEX_CACHE``
+ ``GeoIPOptions`` C API settings, respectively.
+ Defaults to 0 (``GEOIP_STANDARD``).
+
+``country`` The name of the GeoIP country data file. Defaults
+ to ``GeoIP.dat``. Setting this keyword overrides the
+ :setting:`GEOIP_COUNTRY` settings attribute.
+
+``city`` The name of the GeoIP city data file. Defaults to
+ ``GeoLiteCity.dat``. Setting this keyword overrides
+ the :setting:`GEOIP_CITY` settings attribute.
+=================== =======================================================
+
+``GeoIP`` Methods
+=================
+
+Querying
+--------
+
+All the following querying routines may take either a string IP address
+or a fully qualified domain name (FQDN). For example, both
+``'24.124.1.80'`` and ``'djangoproject.com'`` would be valid query
+parameters.
+
+.. method:: GeoIP.city(query)
+
+Returns a dictionary of city information for the given query. Some
+of the values in the dictionary may be undefined (``None``).
+
+.. method:: GeoIPcountry(query)
+
+Returns a dictionary with the country code and country for the given
+query.
+
+.. method:: GeoIP.country_code(query)
+
+Returns only the country code corresponding to the query.
+
+.. method:: GeoIP.country_name(query)
+
+Returns only the country name corresponding to the query.
+
+Coordinate Retrieval
+--------------------
+
+.. method:: GeoIP.coords(query)
+
+Returns a coordinate tuple of (longitude, latitude).
+
+.. method:: GeoIP.lon_lat(query)
+
+Returns a coordinate tuple of (longitude, latitude).
+
+.. method:: GeoIP.lat_lon(query)
+
+Returns a coordinate tuple of (latitude, longitude),
+
+.. method:: GeoIP.geos(query)
+
+Returns a :class:`django.contrib.gis.geos.Point` object corresponding to the query.
+
+Database Information
+--------------------
+
+.. attribute:: GeoIP.country_info
+
+This property returns information about the GeoIP country database.
+
+.. attribute:: GeoIP.city_info
+
+This property returns information about the GeoIP city database.
+
+.. attribute:: GeoIP.info
+
+This property returns information about all GeoIP databases (both city
+and country).
+
+GeoIP-Python API compatibility methods
+----------------------------------------
+
+These methods exist to ease compatibility with any code using MaxMind's
+existing Python API.
+
+.. classmethod:: GeoIP.open(path, cache)
+
+This classmethod instantiates the GeoIP object from the given database path
+and given cache setting.
+
+.. method:: GeoIP.region_by_addr(query)
+
+.. method:: GeoIP.region_by_name(query)
+
+.. method:: GeoIP.record_by_addr(query)
+
+.. method:: GeoIP.record_by_name(query)
+
+.. method:: GeoIP.country_code_by_addr(query)
+
+.. method:: GeoIP.country_code_by_name(query)
+
+.. method:: GeoIP.country_name_by_addr(query)
+
+.. method:: GeoIP.country_name_by_name(query)
+
+.. rubric:: Footnotes
+.. [#] GeoIP(R) is a registered trademark of MaxMind, LLC of Boston, Massachusetts.
diff --git a/parts/django/docs/ref/contrib/gis/geoquerysets.txt b/parts/django/docs/ref/contrib/gis/geoquerysets.txt
new file mode 100644
index 0000000..69f0c02
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/geoquerysets.txt
@@ -0,0 +1,1256 @@
+.. _ref-geoquerysets:
+
+=========================
+GeoQuerySet API Reference
+=========================
+
+.. currentmodule:: django.contrib.gis.db.models
+
+.. class:: GeoQuerySet([model=None])
+
+
+.. _spatial-lookups:
+
+Spatial Lookups
+===============
+
+Just like when using the the :ref:`queryset-api`, interaction
+with ``GeoQuerySet`` by :ref:`chaining filters <chaining-filters>`.
+Instead of the regular Django :ref:`field-lookups`, the
+spatial lookups in this section are available for :class:`GeometryField`.
+
+For an introduction, see the :ref:`spatial lookups introduction
+<spatial-lookups-intro>`. For an overview of what lookups are
+compatible with a particular spatial backend, refer to the
+:ref:`spatial lookup compatibility table <spatial-lookup-compatibility>`.
+
+.. fieldlookup:: bbcontains
+
+bbcontains
+----------
+
+*Availability*: PostGIS, MySQL, SpatiaLite
+
+Tests if the geometry field's bounding box completely contains the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__bbcontains=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``poly ~ geom``
+MySQL ``MBRContains(poly, geom)``
+SpatiaLite ``MbrContains(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: bboverlaps
+
+bboverlaps
+----------
+
+*Availability*: PostGIS, MySQL, SpatiaLite
+
+Tests if the geometry field's bounding box overlaps the lookup geometry's
+bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__bboverlaps=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``poly && geom``
+MySQL ``MBROverlaps(poly, geom)``
+SpatiaLite ``MbrOverlaps(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: contained
+
+contained
+---------
+
+*Availability*: PostGIS, MySQL, SpatiaLite
+
+Tests if the geometry field's bounding box is completely contained by the
+lookup geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__contained=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``poly @ geom``
+MySQL ``MBRWithin(poly, geom)``
+SpatiaLite ``MbrWithin(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: gis-contains
+
+contains
+--------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+Tests if the geometry field spatially contains the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__contains=geom)
+
+========== ============================
+Backend SQL Equivalent
+========== ============================
+PostGIS ``ST_Contains(poly, geom)``
+Oracle ``SDO_CONTAINS(poly, geom)``
+MySQL ``MBRContains(poly, geom)``
+SpatiaLite ``Contains(poly, geom)``
+========== ============================
+
+.. fieldlookup:: contains_properly
+
+contains_properly
+-----------------
+
+.. versionadded:: 1.2
+
+*Availability*: PostGIS
+
+Returns true if the lookup geometry intersects the interior of the
+geometry field, but not the boundary (or exterior). [#fncontainsproperly]_
+
+.. note::
+
+ Requires PostGIS 1.4 and above.
+
+Example::
+
+ Zipcode.objects.filter(poly__contains_properly=geom)
+
+========== ===================================
+Backend SQL Equivalent
+========== ===================================
+PostGIS ``ST_ContainsProperly(poly, geom)``
+========== ===================================
+
+.. fieldlookup:: coveredby
+
+coveredby
+---------
+
+*Availability*: PostGIS, Oracle
+
+Tests if no point in the geometry field is outside the lookup geometry.
+[#fncovers]_
+
+Example::
+
+ Zipcode.objects.filter(poly__coveredby=geom)
+
+========== =============================
+Backend SQL Equivalent
+========== =============================
+PostGIS ``ST_CoveredBy(poly, geom)``
+Oracle ``SDO_COVEREDBY(poly, geom)``
+========== =============================
+
+.. fieldlookup:: covers
+
+covers
+------
+
+*Availability*: PostGIS, Oracle
+
+Tests if no point in the lookup geometry is outside the geometry field.
+[#fncovers]_
+
+Example::
+
+ Zipcode.objects.filter(poly__covers=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``ST_Covers(poly, geom)``
+Oracle ``SDO_COVERS(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: crosses
+
+crosses
+-------
+
+*Availability*: PostGIS, SpatiaLite
+
+Tests if the geometry field spatially crosses the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__crosses=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``ST_Crosses(poly, geom)``
+SpatiaLite ``Crosses(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: disjoint
+
+disjoint
+--------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+Tests if the geometry field is spatially disjoint from the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__disjoint=geom)
+
+========== =================================================
+Backend SQL Equivalent
+========== =================================================
+PostGIS ``ST_Disjoint(poly, geom)``
+Oracle ``SDO_GEOM.RELATE(poly, 'DISJOINT', geom, 0.05)``
+MySQL ``MBRDisjoint(poly, geom)``
+SpatiaLite ``Disjoint(poly, geom)``
+========== =================================================
+
+equals
+------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+.. fieldlookup:: exact
+.. fieldlookup:: same_as
+
+exact, same_as
+--------------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+.. fieldlookup:: intersects
+
+intersects
+----------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+Tests if the geometry field spatially intersects the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__intersects=geom)
+
+========== =================================================
+Backend SQL Equivalent
+========== =================================================
+PostGIS ``ST_Intersects(poly, geom)``
+Oracle ``SDO_OVERLAPBDYINTERSECT(poly, geom)``
+MySQL ``MBRIntersects(poly, geom)``
+SpatiaLite ``Intersects(poly, geom)``
+========== =================================================
+
+.. fieldlookup:: overlaps
+
+overlaps
+--------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+.. fieldlookup:: relate
+
+relate
+------
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+Tests if the geometry field is spatially related to the the lookup geometry by
+the values given in the given pattern. This lookup requires a tuple parameter,
+``(geom, pattern)``; the form of ``pattern`` will depend on the spatial backend:
+
+PostGIS & SpatiaLite
+~~~~~~~~~~~~~~~~~~~~
+On these spatial backends the intersection pattern is a string comprising
+nine characters, which define intersections between the interior, boundary,
+and exterior of the geometry field and the lookup geometry.
+The intersection pattern matrix may only use the following characters:
+``1``, ``2``, ``T``, ``F``, or ``*``. This lookup type allows users to "fine tune"
+a specific geometric relationship consistent with the DE-9IM model. [#fnde9im]_
+
+Example::
+
+ # A tuple lookup parameter is used to specify the geometry and
+ # the intersection pattern (the pattern here is for 'contains').
+ Zipcode.objects.filter(poly__relate(geom, 'T*T***FF*'))
+
+PostGIS SQL equivalent::
+
+ SELECT ... WHERE ST_Relate(poly, geom, 'T*T***FF*')
+
+SpatiaLite SQL equivalent::
+
+ SELECT ... WHERE Relate(poly, geom, 'T*T***FF*')
+
+Oracle
+~~~~~~
+
+Here the relation pattern is compreised at least one of the nine relation
+strings: ``TOUCH``, ``OVERLAPBDYDISJOINT``, ``OVERLAPBDYINTERSECT``,
+``EQUAL``, ``INSIDE``, ``COVEREDBY``, ``CONTAINS``, ``COVERS``, ``ON``, and
+``ANYINTERACT``. Multiple strings may be combined with the logical Boolean
+operator OR, for example, ``'inside+touch'``. [#fnsdorelate]_ The relation
+strings are case-insensitive.
+
+Example::
+
+ Zipcode.objects.filter(poly__relate(geom, 'anyinteract'))
+
+Oracle SQL equivalent::
+
+ SELECT ... WHERE SDO_RELATE(poly, geom, 'anyinteract')
+
+.. fieldlookup:: touches
+
+touches
+-------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+Tests if the geometry field spatially touches the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__touches=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``ST_Touches(poly, geom)``
+MySQL ``MBRTouches(poly, geom)``
+Oracle ``SDO_TOUCH(poly, geom)``
+SpatiaLite ``Touches(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: within
+
+within
+------
+
+*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
+
+Tests if the geometry field is spatially within the lookup geometry.
+
+Example::
+
+ Zipcode.objects.filter(poly__within=geom)
+
+========== ==========================
+Backend SQL Equivalent
+========== ==========================
+PostGIS ``ST_Within(poly, geom)``
+MySQL ``MBRWithin(poly, geom)``
+Oracle ``SDO_INSIDE(poly, geom)``
+SpatiaLite ``Within(poly, geom)``
+========== ==========================
+
+.. fieldlookup:: left
+
+left
+----
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box is strictly to the left of the
+lookup geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__left=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly << geom
+
+.. fieldlookup:: right
+
+right
+-----
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box is strictly to the right of the
+lookup geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__right=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly >> geom
+
+.. fieldlookup:: overlaps_left
+
+overlaps_left
+-------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box overlaps or is to the left of the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__overlaps_left=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly &< geom
+
+
+.. fieldlookup:: overlaps_right
+
+overlaps_right
+--------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box overlaps or is to the right of the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__overlaps_right=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly &> geom
+
+.. fieldlookup:: overlaps_above
+
+overlaps_above
+--------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box overlaps or is above the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__overlaps_above=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly |&> geom
+
+.. fieldlookup:: overlaps_below
+
+overlaps_below
+--------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box overlaps or is below the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__overlaps_below=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly &<| geom
+
+.. fieldlookup:: strictly_above
+
+strictly_above
+--------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box is strictly above the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__strictly_above=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly |>> geom
+
+.. fieldlookup:: strictly_below
+
+strictly_below
+--------------
+
+*Availability*: PostGIS
+
+Tests if the geometry field's bounding box is strictly above the lookup
+geometry's bounding box.
+
+Example::
+
+ Zipcode.objects.filter(poly__strictly_above=geom)
+
+PostGIS equivalent::
+
+ SELECT ... WHERE poly |>> geom
+
+
+.. _distance-lookups:
+
+Distance Lookups
+================
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+For an overview on performing distance queries, please refer to
+the :ref:`distance queries introduction <distance-queries>`.
+
+Distance lookups take the following form::
+
+ <field>__<distance lookup>=(<geometry>, <distance value>[, 'spheroid'])
+
+The value passed into a distance lookup is a tuple; the first two
+values are mandatory, and are the geometry to calculate distances to,
+and a distance value (either a number in units of the field or a
+:class:`~django.contrib.gis.measure.Distance` object). On every
+distance lookup but :lookup:`dwithin`, an optional
+third element, ``'spheroid'``, may be included to tell GeoDjango
+to use the more accurate spheroid distance calculation functions on
+fields with a geodetic coordinate system (e.g., ``ST_Distance_Spheroid``
+would be used instead of ``ST_Distance_Sphere``).
+
+.. fieldlookup:: distance_gt
+
+distance_gt
+-----------
+
+Returns models where the distance to the geometry field from the lookup
+geometry is greater than the given distance value.
+
+Example::
+
+ Zipcode.objects.filter(poly__distance_gt=(geom, D(m=5)))
+
+========== ===============================================
+Backend SQL Equivalent
+========== ===============================================
+PostGIS ``ST_Distance(poly, geom) > 5``
+Oracle ``SDO_GEOM.SDO_DISTANCE(poly, geom, 0.05) > 5``
+SpatiaLite ``Distance(poly, geom) > 5``
+========== ===============================================
+
+.. fieldlookup:: distance_gte
+
+distance_gte
+------------
+
+Returns models where the distance to the geometry field from the lookup
+geometry is greater than or equal to the given distance value.
+
+Example::
+
+ Zipcode.objects.filter(poly__distance_gte=(geom, D(m=5)))
+
+========== ================================================
+Backend SQL Equivalent
+========== ================================================
+PostGIS ``ST_Distance(poly, geom) >= 5``
+Oracle ``SDO_GEOM.SDO_DISTANCE(poly, geom, 0.05) >= 5``
+SpatiaLite ``Distance(poly, geom) >= 5``
+========== ================================================
+
+.. fieldlookup:: distance_lt
+
+distance_lt
+-----------
+
+Returns models where the distance to the geometry field from the lookup
+geometry is less than the given distance value.
+
+Example::
+
+ Zipcode.objects.filter(poly__distance_lt=(geom, D(m=5)))
+
+========== ===============================================
+Backend SQL Equivalent
+========== ===============================================
+PostGIS ``ST_Distance(poly, geom) < 5``
+Oracle ``SDO_GEOM.SDO_DISTANCE(poly, geom, 0.05) < 5``
+SpatiaLite ``Distance(poly, geom) < 5``
+========== ===============================================
+
+.. fieldlookup:: distance_lte
+
+distance_lte
+------------
+
+Returns models where the distance to the geometry field from the lookup
+geometry is less than or equal to the given distance value.
+
+Example::
+
+ Zipcode.objects.filter(poly__distance_lte=(geom, D(m=5)))
+
+========== ================================================
+Backend SQL Equivalent
+========== ================================================
+PostGIS ``ST_Distance(poly, geom) <= 5``
+Oracle ``SDO_GEOM.SDO_DISTANCE(poly, geom, 0.05) <= 5``
+SpatiaLite ``Distance(poly, geom) <= 5``
+========== ================================================
+
+.. fieldlookup:: dwithin
+
+dwithin
+-------
+
+Returns models where the distance to the geometry field from the
+lookup geometry are within the given distance from one another.
+
+Example::
+
+ Zipcode.objects.filter(poly__dwithin=(geom, D(m=5)))
+
+========== ======================================
+Backend SQL Equivalent
+========== ======================================
+PostGIS ``ST_DWithin(poly, geom, 5)``
+Oracle ``SDO_WITHIN_DISTANCE(poly, geom, 5)``
+========== ======================================
+
+.. note::
+
+ This lookup is not available on SpatiaLite.
+
+.. fieldlookup:: equals
+
+
+``GeoQuerySet`` Methods
+=======================
+
+``GeoQuerySet`` methods specify that a spatial operation be performed
+on each patial operation on each geographic
+field in the queryset and store its output in a new attribute on the model
+(which is generally the name of the ``GeoQuerySet`` method).
+
+There are also aggregate ``GeoQuerySet`` methods which return a single value
+instead of a queryset. This section will describe the API and availability
+of every ``GeoQuerySet`` method available in GeoDjango.
+
+.. note::
+
+ What methods are available depend on your spatial backend. See
+ the :ref:`compatibility table <geoqueryset-method-compatibility>`
+ for more details.
+
+With a few exceptions, the following keyword arguments may be used with all
+``GeoQuerySet`` methods:
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``field_name`` By default, ``GeoQuerySet`` methods use the first
+ geographic field encountered in the model. This
+ keyword should be used to specify another
+ geographic field (e.g., ``field_name='point2'``)
+ when there are multiple geographic fields in a model.
+
+ On PostGIS, the ``field_name`` keyword may also be
+ used on geometry fields in models that are related
+ via a ``ForeignKey`` relation (e.g.,
+ ``field_name='related__point'``).
+
+``model_att`` By default, ``GeoQuerySet`` methods typically attach
+ their output in an attribute with the same name as
+ the ``GeoQuerySet`` method. Setting this keyword
+ with the desired attribute name will override this
+ default behavior. For example,
+ ``qs = Zipcode.objects.centroid(model_att='c')`` will
+ attach the centroid of the ``Zipcode`` geometry field
+ in a ``c`` attribute on every model rather than in a
+ ``centroid`` attribute.
+
+ This keyword is required if
+ a method name clashes with an existing
+ ``GeoQuerySet`` method -- if you wanted to use the
+ ``area()`` method on model with a ``PolygonField``
+ named ``area``, for example.
+===================== =====================================================
+
+Measurement
+-----------
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+``area``
+~~~~~~~~
+
+.. method:: GeoQuerySet.area(**kwargs)
+
+Returns the area of the geographic field in an ``area`` attribute on
+each element of this GeoQuerySet.
+
+``distance``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.distance(geom, **kwargs)
+
+This method takes a geometry as a parameter, and attaches a ``distance``
+attribute to every model in the returned queryset that contains the
+distance (as a :class:`~django.contrib.gis.measure.Distance` object) to the given geometry.
+
+In the following example (taken from the `GeoDjango distance tests`__),
+the distance from the `Tasmanian`__ city of Hobart to every other
+:class:`PointField` in the ``AustraliaCity`` queryset is calculated::
+
+ >>> pnt = AustraliaCity.objects.get(name='Hobart').point
+ >>> for city in AustraliaCity.objects.distance(pnt): print city.name, city.distance
+ Wollongong 990071.220408 m
+ Shellharbour 972804.613941 m
+ Thirroul 1002334.36351 m
+ Mittagong 975691.632637 m
+ Batemans Bay 834342.185561 m
+ Canberra 598140.268959 m
+ Melbourne 575337.765042 m
+ Sydney 1056978.87363 m
+ Hobart 0.0 m
+ Adelaide 1162031.83522 m
+ Hillsdale 1049200.46122 m
+
+.. note::
+
+ Because the ``distance`` attribute is a
+ :class:`~django.contrib.gis.measure.Distance` object, you can easily express
+ the value in the units of your choice. For example, ``city.distance.mi`` is
+ the distance value in miles and ``city.distance.km`` is the distance value
+ in kilometers. See the :ref:`ref-measure` for usage details and the list of
+ :ref:`supported_units`.
+
+__ http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/tests/distapp/models.py
+__ http://en.wikipedia.org/wiki/Tasmania
+
+``length``
+~~~~~~~~~~
+
+.. method:: GeoQuerySet.length(**kwargs)
+
+Returns the length of the geometry field in a ``length`` attribute
+(a :class:`~django.contrib.gis.measure.Distance` object) on each model in
+the queryset.
+
+``perimeter``
+~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.perimeter(**kwargs)
+
+Returns the perimeter of the geometry field in a ``perimeter`` attribute
+(a :class:`~django.contrib.gis.measure.Distance` object) on each model in
+the queryset.
+
+Geometry Relationships
+----------------------
+
+The following methods take no arguments, and attach geometry objects
+each element of the :class:`GeoQuerySet` that is the result of relationship
+function evaluated on the the geometry field.
+
+``centroid``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.centroid(**kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+Returns the ``centroid`` value for the geographic field in a ``centroid``
+attribute on each element of the ``GeoQuerySet``.
+
+``envelope``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.envelope(**kwargs)
+
+*Availability*: PostGIS, SpatiaLite
+
+Returns a geometry representing the bounding box of the geometry field in
+an ``envelope`` attribute on each element of the ``GeoQuerySet``.
+
+``point_on_surface``
+~~~~~~~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.point_on_surface(**kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+Returns a Point geometry guaranteed to lie on the surface of the
+geometry field in a ``point_on_surface`` attribute on each element
+of the queryset; otherwise sets with None.
+
+Geometry Editors
+----------------
+
+``force_rhr``
+~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.force_rhr(**kwargs)
+
+.. versionadded:: 1.2
+
+*Availability*: PostGIS
+
+Returns a modified version of the polygon/multipolygon in which all
+of the vertices follow the Right-Hand-Rule, and attaches as a
+``force_rhr`` attribute on each element of the queryset.
+
+``reverse_geom``
+~~~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.reverse_geom(**kwargs)
+
+.. versionadded:: 1.2
+
+*Availability*: PostGIS, Oracle
+
+Reverse the coordinate order of the geometry field, and attaches as a
+``reverse`` attribute on each element of the queryset.
+
+``scale``
+~~~~~~~~~
+
+.. method:: GeoQuerySet.scale(x, y, z=0.0, **kwargs)
+
+*Availability*: PostGIS, SpatiaLite
+
+``snap_to_grid``
+~~~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.snap_to_grid(*args, **kwargs)
+
+.. versionadded:: 1.1
+
+Snap all points of the input geometry to the grid. How the
+geometry is snapped to the grid depends on how many numeric
+(either float, integer, or long) arguments are given.
+
+=================== =====================================================
+Number of Arguments Description
+=================== =====================================================
+1 A single size to snap bot the X and Y grids to.
+2 X and Y sizes to snap the grid to.
+4 X, Y sizes and the corresponding X, Y origins.
+=================== =====================================================
+
+``transform``
+~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.transform(srid=4326, **kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+The ``transform`` method transforms the geometry field of a model to the spatial
+reference system specified by the ``srid`` parameter. If no ``srid`` is given,
+then 4326 (WGS84) is used by default.
+
+.. note::
+
+ Unlike other ``GeoQuerySet`` methods, ``transform`` stores its output
+ "in-place". In other words, no new attribute for the transformed
+ geometry is placed on the models.
+
+.. note::
+
+ What spatial reference system an integer SRID corresponds to may depend on
+ the spatial database used. In other words, the SRID numbers used for Oracle
+ are not necessarily the same as those used by PostGIS.
+
+Example::
+
+ >>> qs = Zipcode.objects.all().transform() # Transforms to WGS84
+ >>> qs = Zipcode.objects.all().transform(32140) # Transforming to "NAD83 / Texas South Central"
+ >>> print qs[0].poly.srid
+ 32140
+ >>> print qs[0].poly
+ POLYGON ((234055.1698884720099159 4937796.9232223574072123 ...
+
+``translate``
+~~~~~~~~~~~~~
+.. method:: GeoQuerySet.translate(x, y, z=0.0, **kwargs)
+
+*Availability*: PostGIS, SpatiaLite
+
+Translates the geometry field to a new location using the given numeric
+parameters as offsets.
+
+Geometry Operations
+-------------------
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+The following methods all take a geometry as a parameter and attach a geometry
+to each element of the ``GeoQuerySet`` that is the result of the operation.
+
+``difference``
+~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.difference(geom)
+
+Returns the spatial difference of the geographic field with the given
+geometry in a ``difference`` attribute on each element of the
+``GeoQuerySet``.
+
+
+``intersection``
+~~~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.intersection(geom)
+
+Returns the spatial intersection of the geographic field with the
+given geometry in an ``intersection`` attribute on each element of the
+``GeoQuerySet``.
+
+``sym_difference``
+~~~~~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.sym_difference(geom)
+
+Returns the symmetric difference of the geographic field with the
+given geometry in a ``sym_difference`` attribute on each element of the
+``GeoQuerySet``.
+
+``union``
+~~~~~~~~~
+
+.. method:: GeoQuerySet.union(geom)
+
+Returns the union of the geographic field with the given
+geometry in an ``union`` attribute on each element of the
+``GeoQuerySet``.
+
+Geometry Output
+---------------
+
+The following ``GeoQuerySet`` methods will return an attribute that has the value
+of the geometry field in each model converted to the requested output format.
+
+``geohash``
+~~~~~~~~~~~
+
+.. method:: GeoQuerySet.geohash(preceision=20, **kwargs)
+
+.. versionadded:: 1.2
+
+Attaches a ``geohash`` attribute to every model the the queryset
+containing the `GeoHash`__ representation of the geometry.
+
+__ http://geohash.org/
+
+``geojson``
+~~~~~~~~~~~
+
+.. method:: GeoQuerySet.geojson(**kwargs)
+
+.. versionadded:: 1.1
+
+*Availability*: PostGIS
+
+Attaches a ``geojson`` attribute to every model in the queryset that contains the
+`GeoJSON`__ representation of the geometry.
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``precision`` It may be used to specify the number of significant
+ digits for the coordinates in the GeoJSON
+ representation -- the default value is 8.
+
+``crs`` Set this to ``True`` if you want the coordinate
+ reference system to be included in the returned
+ GeoJSON.
+
+``bbox`` Set this to ``True`` if you want the bounding box
+ to be included in the returned GeoJSON.
+===================== =====================================================
+
+__ http://geojson.org/
+
+``gml``
+~~~~~~~
+
+.. method:: GeoQuerySet.gml(**kwargs)
+
+*Availability*: PostGIS, Oracle
+
+Attaches a ``gml`` attribute to every model in the queryset that contains the
+`Geographic Markup Language (GML)`__ representation of the geometry.
+
+Example::
+
+ >>> qs = Zipcode.objects.all().gml()
+ >>> print qs[0].gml
+ <gml:Polygon srsName="EPSG:4326"><gml:OuterBoundaryIs>-147.78711,70.245363 ... -147.78711,70.245363</gml:OuterBoundaryIs></gml:Polygon>
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``precision`` This keyword is for PostGIS only. It may be used
+ to specify the number of significant digits for the
+ coordinates in the GML representation -- the default
+ value is 8.
+
+``version`` This keyword is for PostGIS only. It may be used to
+ specify the GML version used, and may only be values
+ of 2 or 3. The default value is 2.
+===================== =====================================================
+
+__ http://en.wikipedia.org/wiki/Geography_Markup_Language
+
+``kml``
+~~~~~~~
+
+.. method:: GeoQuerySet.kml(**kwargs)
+
+*Availability*: PostGIS
+
+Attaches a ``kml`` attribute to every model in the queryset that contains the
+`Keyhole Markup Language (KML)`__ representation of the geometry fields. It
+should be noted that the contents of the KML are transformed to WGS84 if
+necessary.
+
+Example::
+
+ >>> qs = Zipcode.objects.all().kml()
+ >>> print qs[0].kml
+ <Polygon><outerBoundaryIs><LinearRing><coordinates>-103.04135,36.217596,0 ... -103.04135,36.217596,0</coordinates></LinearRing></outerBoundaryIs></Polygon>
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``precision`` This keyword may be used to specify the number of
+ significant digits for the coordinates in the KML
+ representation -- the default value is 8.
+===================== =====================================================
+
+__ http://code.google.com/apis/kml/documentation/
+
+``svg``
+~~~~~~~
+
+.. method:: GeoQuerySet.svg(**kwargs)
+
+*Availability*: PostGIS, SpatiaLite
+
+Attaches a ``svg`` attribute to every model in the queryset that contains
+the `Scalable Vector Graphics (SVG)`__ path data of the geometry fields.
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``relative`` If set to ``True``, the path data will be implemented
+ in terms of relative moves. Defaults to ``False``,
+ meaning that absolute moves are used instead.
+
+``precision`` This keyword may be used to specify the number of
+ significant digits for the coordinates in the SVG
+ representation -- the default value is 8.
+===================== =====================================================
+
+__ http://www.w3.org/Graphics/SVG/
+
+Miscellaneous
+-------------
+
+``mem_size``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.mem_size(**kwargs)
+
+*Availability*: PostGIS
+
+Returns the memory size (number of bytes) that the geometry field takes
+in a ``mem_size`` attribute on each element of the ``GeoQuerySet``.
+
+``num_geom``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.num_geom(**kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+Returns the number of geometries in a ``num_geom`` attribute on
+each element of the ``GeoQuerySet`` if the geometry field is a
+collection (e.g., a ``GEOMETRYCOLLECTION`` or ``MULTI*`` field);
+otherwise sets with ``None``.
+
+``num_points``
+~~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.num_points(**kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+Returns the number of points in the first linestring in the
+geometry field in a ``num_points`` attribute on each element of
+the ``GeoQuerySet``; otherwise sets with ``None``.
+
+Spatial Aggregates
+==================
+.. versionadded:: 1.1
+
+Aggregate Methods
+-----------------
+
+``collect``
+~~~~~~~~~~~
+
+.. method:: GeoQuerySet.collect(**kwargs)
+
+.. versionadded:: 1.1
+
+*Availability*: PostGIS
+
+Returns a ``GEOMETRYCOLLECTION`` or a ``MULTI`` geometry object from the geometry
+column. This is analagous to a simplified version of the :meth:`GeoQuerySet.unionagg` method,
+except it can be several orders of magnitude faster than peforming a union because
+it simply rolls up geometries into a collection or multi object, not caring about
+dissolving boundaries.
+
+``extent``
+~~~~~~~~~~
+
+.. method:: GeoQuerySet.extent(**kwargs)
+
+*Availability*: PostGIS, Oracle
+
+Returns the extent of the ``GeoQuerySet`` as a four-tuple, comprising the
+lower left coordinate and the upper right coordinate.
+
+Example::
+
+ >>> qs = City.objects.filter(name__in=('Houston', 'Dallas'))
+ >>> print qs.extent()
+ (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820)
+
+``extent3d``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.extent3d(**kwargs)
+
+.. versionadded:: 1.2
+
+*Availability*: PostGIS
+
+Returns the 3D extent of the ``GeoQuerySet`` as a six-tuple, comprising
+the lower left coordinate and upper right coordinate.
+
+Example::
+
+ >>> qs = City.objects.filter(name__in=('Houston', 'Dallas'))
+ >>> print qs.extent3d()
+ (-96.8016128540039, 29.7633724212646, 0, -95.3631439208984, 32.782058715820, 0)
+
+``make_line``
+~~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.make_line(**kwargs)
+
+*Availability*: PostGIS
+
+Returns a ``LineString`` constructed from the point field geometries in the
+``GeoQuerySet``. Currently, ordering the queryset has no effect.
+
+Example::
+
+ >>> print City.objects.filter(name__in=('Houston', 'Dallas')).make_line()
+ LINESTRING (-95.3631510000000020 29.7633739999999989, -96.8016109999999941 32.7820570000000018)
+
+``unionagg``
+~~~~~~~~~~~~
+
+.. method:: GeoQuerySet.unionagg(**kwargs)
+
+*Availability*: PostGIS, Oracle, SpatiaLite
+
+This method returns a :class:`~django.contrib.gis.geos.GEOSGeometry` object
+comprising the union of every geometry in the queryset. Please note that
+use of ``unionagg`` is processor intensive and may take a significant amount
+of time on large querysets.
+
+.. note::
+
+ If the computation time for using this method is too expensive,
+ consider using :meth:`GeoQuerySet.collect` instead.
+
+Example::
+
+ >>> u = Zipcode.objects.unionagg() # This may take a long time.
+ >>> u = Zipcode.objects.filter(poly__within=bbox).unionagg() # A more sensible approach.
+
+===================== =====================================================
+Keyword Argument Description
+===================== =====================================================
+``tolerance`` This keyword is for Oracle only. It is for the
+ tolerance value used by the ``SDOAGGRTYPE``
+ procedure; the `Oracle documentation`__ has more
+ details.
+===================== =====================================================
+
+__ http://download.oracle.com/docs/html/B14255_01/sdo_intro.htm#sthref150
+
+Aggregate Functions
+-------------------
+
+Example::
+
+ >>> from django.contrib.gis.db.models import Extent, Union
+ >>> WorldBorders.objects.aggregate(Extent('mpoly'), Union('mpoly'))
+
+``Collect``
+~~~~~~~~~~~
+
+.. class:: Collect(geo_field)
+
+Returns the same as the :meth:`GeoQuerySet.collect` aggregate method.
+
+``Extent``
+~~~~~~~~~~
+.. class:: Extent(geo_field)
+
+
+Returns the same as the :meth:`GeoQuerySet.extent` aggregate method.
+
+``Extent3D``
+~~~~~~~~~~~~
+
+.. class:: Extent3D(geo_field)
+
+.. versionadded:: 1.2
+
+Returns the same as the :meth:`GeoQuerySet.extent3d` aggregate method.
+
+``MakeLine``
+~~~~~~~~~~~~
+
+.. class:: MakeLine(geo_field)
+
+Returns the same as the :meth:`GeoQuerySet.make_line` aggregate method.
+
+``Union``
+~~~~~~~~~
+
+.. class:: Union(geo_field)
+
+Returns the same as the :meth:`GeoQuerySet.union` aggregate method.
+
+.. rubric:: Footnotes
+.. [#fnde9im] *See* `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, at Ch. 2.1.13.2, p. 2-13 (The Dimensionally Extended Nine-Intersection Model).
+.. [#fnsdorelate] *See* `SDO_RELATE documentation <http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14255/sdo_operat.htm#sthref845>`_, from Ch. 11 of the Oracle Spatial User's Guide and Manual.
+.. [#fncovers] For an explanation of this routine, read `Quirks of the "Contains" Spatial Predicate <http://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html>`_ by Martin Davis (a PostGIS developer).
+.. [#fncontainsproperly] Refer to the PostGIS ``ST_ContainsProperly`` `documentation <http://postgis.refractions.net/documentation/manual-1.4/ST_ContainsProperly.html>`_ for more details.
diff --git a/parts/django/docs/ref/contrib/gis/geos.txt b/parts/django/docs/ref/contrib/gis/geos.txt
new file mode 100644
index 0000000..06a88a8
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/geos.txt
@@ -0,0 +1,911 @@
+.. _ref-geos:
+
+========
+GEOS API
+========
+
+.. module:: django.contrib.gis.geos
+ :synopsis: GeoDjango's high-level interface to the GEOS library.
+
+Background
+==========
+
+What is GEOS?
+-------------
+
+`GEOS`__ stands for **G**\ eometry **E**\ ngine - **O**\ pen **S**\ ource,
+and is a C++ library, ported from the `Java Topology Suite`__. GEOS
+implements the OpenGIS `Simple Features for SQL`__ spatial predicate functions
+and spatial operators. GEOS, now an OSGeo project, was initially developed and
+maintained by `Refractions Research`__ of Victoria, Canada.
+
+__ http://trac.osgeo.org/geos/
+__ http://sourceforge.net/projects/jts-topo-suite/
+__ http://www.opengeospatial.org/standards/sfs
+__ http://www.refractions.net/
+
+Features
+--------
+
+GeoDjango implements a high-level Python wrapper for the GEOS library, its
+features include:
+
+* A BSD-licensed interface to the GEOS geometry routines, implemented purely
+ in Python using ``ctypes``.
+* Loosely-coupled to GeoDjango. For example, :class:`GEOSGeometry` objects
+ may be used outside of a django project/application. In other words,
+ no need to have ``DJANGO_SETTINGS_MODULE`` set or use a database, etc.
+* Mutability: :class:`GEOSGeometry` objects may be modified.
+* Cross-platform and tested; compatible with Windows, Linux, Solaris, and Mac
+ OS X platforms.
+
+.. _geos-tutorial:
+
+Tutorial
+========
+
+This section contains a brief introduction and tutorial to using
+:class:`GEOSGeometry` objects.
+
+Creating a Geometry
+-------------------
+
+:class:`GEOSGeometry` objects may be created in a few ways. The first is
+to simply instantiate the object on some spatial input -- the following
+are examples of creating the same geometry from WKT, HEX, WKB, and GeoJSON::
+
+ >>> from django.contrib.gis.geos import GEOSGeometry
+ >>> pnt = GEOSGeometry('POINT(5 23)') # WKT
+ >>> pnt = GEOSGeometry('010100000000000000000014400000000000003740') # HEX
+ >>> pnt = GEOSGeometry(buffer('\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x007@'))
+ >>> pnt = GEOSGeometry('{ "type": "Point", "coordinates": [ 5.000000, 23.000000 ] }') # GeoJSON
+
+Another option is to use the constructor for the specific geometry type
+that you wish to create. For example, a :class:`Point` object may be
+created by passing in the X and Y coordinates into its constructor::
+
+ >>> from django.contrib.gis.geos import Point
+ >>> pnt = Point(5, 23)
+
+Finally, there are :func:`fromstr` and :func:`fromfile` factory methods, which
+return a :class:`GEOSGeometry` object from an input string or a file::
+
+ >>> from django.contrib.gis.geos import fromstr, fromfile
+ >>> pnt = fromstr('POINT(5 23)')
+ >>> pnt = fromfile('/path/to/pnt.wkt')
+ >>> pnt = fromfile(open('/path/to/pnt.wkt'))
+
+Geometries are Pythonic
+-----------------------
+:class:`GEOSGeometry` objects are 'Pythonic', in other words components may
+be accessed, modified, and iterated over using standard Python conventions.
+For example, you can iterate over the coordinates in a :class:`Point`::
+
+ >>> pnt = Point(5, 23)
+ >>> [coord for coord in pnt]
+ [5.0, 23.0]
+
+With any geometry object, the :attr:`GEOSGeometry.coords` property
+may be used to get the geometry coordinates as a Python tuple::
+
+ >>> pnt.coords
+ (5.0, 23.0)
+
+You can get/set geometry components using standard Python indexing
+techniques. However, what is returned depends on the geometry type
+of the object. For example, indexing on a :class:`LineString`
+returns a coordinate tuple::
+
+ >>> from django.contrib.gis.geos import LineString
+ >>> line = LineString((0, 0), (0, 50), (50, 50), (50, 0), (0, 0))
+ >>> line[0]
+ (0.0, 0.0)
+ >>> line[-2]
+ (50.0, 0.0)
+
+Whereas indexing on a :class:`Polygon` will return the ring
+(a :class:`LinearRing` object) corresponding to the index::
+
+ >>> from django.contrib.gis.geos import Polygon
+ >>> poly = Polygon( ((0.0, 0.0), (0.0, 50.0), (50.0, 50.0), (50.0, 0.0), (0.0, 0.0)) )
+ >>> poly[0]
+ <LinearRing object at 0x1044395b0>
+ >>> poly[0][-2] # second-to-last coordinate of external ring
+ (50.0, 0.0)
+
+In addition, coordinates/components of the geometry may added or modified,
+just like a Python list::
+
+ >>> line[0] = (1.0, 1.0)
+ >>> line.pop()
+ (0.0, 0.0)
+ >>> line.append((1.0, 1.0))
+ >>> line.coords
+ ((1.0, 1.0), (0.0, 50.0), (50.0, 50.0), (50.0, 0.0), (1.0, 1.0))
+
+Geometry Objects
+================
+
+``GEOSGeometry``
+----------------
+
+.. class:: GEOSGeometry(geo_input[, srid=None])
+
+ :param geo_input: Geometry input value
+ :type geo_input: string or buffer
+ :param srid: spatial reference identifier
+ :type srid: integer
+
+This is the base class for all GEOS geometry objects. It initializes on the
+given ``geo_input`` argument, and then assumes the proper geometry subclass
+(e.g., ``GEOSGeometry('POINT(1 1)')`` will create a :class:`Point` object).
+
+The following input formats, along with their corresponding Python types,
+are accepted:
+
+============= ======================
+Format Input Type
+============= ======================
+WKT / EWKT ``str`` or ``unicode``
+HEX / HEXEWKB ``str`` or ``unicode``
+WKB / EWKB ``buffer``
+GeoJSON ``str`` or ``unicode``
+============= ======================
+
+Properties
+~~~~~~~~~~
+
+.. attribute:: GEOSGeometry.coords
+
+Returns the coordinates of the geometry as a tuple.
+
+.. attribute:: GEOSGeometry.empty
+
+Returns whether or not the set of points in the geometry is empty.
+
+.. attribute:: GEOSGeometry.geom_type
+
+Returns a string corresponding to the type of geometry. For example::
+
+ >>> pnt = GEOSGeometry('POINT(5 23)')
+ >>> pnt.geom_type
+ 'Point'
+
+.. attribute:: GEOSGeometry.geom_typeid
+
+Returns the GEOS geometry type identification number. The following table
+shows the value for each geometry type:
+
+=========================== ========
+Geometry ID
+=========================== ========
+:class:`Point` 0
+:class:`LineString` 1
+:class:`LinearRing` 2
+:class:`Polygon` 3
+:class:`MultiPoint` 4
+:class:`MultiLineString` 5
+:class:`MultiPolygon` 6
+:class:`GeometryCollection` 7
+=========================== ========
+
+.. attribute:: GEOSGeometry.num_coords
+
+Returns the number of coordinates in the geometry.
+
+.. attribute:: GEOSGeometry.num_geom
+
+Returns the number of geometries in this geometry. In other words, will
+return 1 on anything but geometry collections.
+
+.. attribute:: GEOSGeometry.hasz
+
+Returns a boolean indicating whether the geometry is three-dimensional.
+
+.. attribute:: GEOSGeometry.ring
+
+Returns a boolean indicating whether the geometry is a ``LinearRing``.
+
+.. attribute:: GEOSGeometry.simple
+
+Returns a boolean indicating whether the geometry is 'simple'. A geometry
+is simple if and only if it does not intersect itself (except at boundary
+points). For example, a :class:`LineString` object is not simple if it
+intersects itself. Thus, :class:`LinearRing` and :class`Polygon` objects
+are always simple because they do cannot intersect themselves, by
+definition.
+
+.. attribute:: GEOSGeometry.valid
+
+Returns a boolean indicating whether the geometry is valid.
+
+.. attribute:: GEOSGeometry.srid
+
+Property that may be used to retrieve or set the SRID associated with the
+geometry. For example::
+
+ >>> pnt = Point(5, 23)
+ >>> print pnt.srid
+ None
+ >>> pnt.srid = 4326
+ >>> pnt.srid
+ 4326
+
+Output Properties
+~~~~~~~~~~~~~~~~~
+
+The properties in this section export the :class:`GEOSGeometry` object into
+a different. This output may be in the form of a string, buffer, or even
+another object.
+
+.. attribute:: GEOSGeometry.ewkt
+
+Returns the "extended" Well-Known Text of the geometry. This representation
+is specific to PostGIS and is a super set of the OGC WKT standard. [#fnogc]_
+Essentially the SRID is prepended to the WKT representation, for example
+``SRID=4326;POINT(5 23)``.
+
+.. note::
+
+ The output from this property does not include the 3dm, 3dz, and 4d
+ information that PostGIS supports in its EWKT representations.
+
+.. attribute:: GEOSGeometry.hex
+
+Returns the WKB of this Geometry in hexadecimal form. Please note
+that the SRID and Z values are not included in this representation
+because it is not a part of the OGC specification (use the
+:attr:`GEOSGeometry.hexewkb` property instead).
+
+.. attribute:: GEOSGeometry.hexewkb
+
+.. versionadded:: 1.2
+
+Returns the EWKB of this Geometry in hexadecimal form. This is an
+extension of the WKB specification that includes SRID and Z values
+that are a part of this geometry.
+
+.. note::
+
+ GEOS 3.1 is *required* if you want valid 3D HEXEWKB.
+
+.. attribute:: GEOSGeometry.json
+
+Returns the GeoJSON representation of the geometry.
+
+.. note::
+
+ Requires GDAL.
+
+.. attribute:: GEOSGeometry.geojson
+
+Alias for :attr:`GEOSGeometry.json`.
+
+.. attribute:: GEOSGeometry.kml
+
+Returns a `KML`__ (Keyhole Markup Language) representation of the
+geometry. This should only be used for geometries with an SRID of
+4326 (WGS84), but this restriction is not enforced.
+
+.. attribute:: GEOSGeometry.ogr
+
+Returns an :class:`~django.contrib.gis.gdal.OGRGeometry` object
+correspondg to the GEOS geometry.
+
+.. note::
+
+ Requires GDAL.
+
+.. _wkb:
+
+.. attribute:: GEOSGeometry.wkb
+
+Returns the WKB (Well-Known Binary) representation of this Geometry
+as a Python buffer. SRID and Z values are not included, use the
+:attr:`GEOSGeometry.ewkb` property instead.
+
+.. _ewkb:
+
+.. attribute:: GEOSGeometry.ewkb
+
+.. versionadded:: 1.2
+
+Return the EWKB representation of this Geometry as a Python buffer.
+This is an extension of the WKB specification that includes any SRID
+and Z values that are a part of this geometry.
+
+.. note::
+
+ GEOS 3.1 is *required* if you want valid 3D EWKB.
+
+.. attribute:: GEOSGeometry.wkt
+
+Returns the Well-Known Text of the geometry (an OGC standard).
+
+__ http://code.google.com/apis/kml/documentation/
+
+Spatial Predicate Methods
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All of the following spatial predicate methods take another
+:class:`GEOSGeometry` instance (``other``) as a parameter, and
+return a boolean.
+
+.. method:: GEOSGeometry.contains(other)
+
+Returns ``True`` if :meth:`GEOSGeometry.within` is ``False``.
+
+.. method:: GEOSGeometry.crosses(other)
+
+Returns ``True`` if the DE-9IM intersection matrix for the two Geometries
+is ``T*T******`` (for a point and a curve,a point and an area or a line
+and an area) ``0********`` (for two curves).
+
+.. method:: GEOSGeometry.disjoint(other)
+
+Returns ``True`` if the DE-9IM intersection matrix for the two geometries
+is ``FF*FF****``.
+
+.. method:: GEOSGeometry.equals(other)
+
+Returns ``True`` if the DE-9IM intersection matrix for the two geometries
+is ``T*F**FFF*``.
+
+.. method:: GEOSGeometry.equals_exact(other, tolerance=0)
+
+Returns true if the two geometries are exactly equal, up to a
+specified tolerance. The ``tolerance`` value should be a floating
+point number representing the error tolerance in the comparison, e.g.,
+``poly1.equals_exact(poly2, 0.001)`` will compare equality to within
+one thousandth of a unit.
+
+.. method:: GEOSGeometry.intersects(other)
+
+Returns ``True`` if :meth:`GEOSGeometry.disjoint` is ``False``.
+
+.. method:: GEOSGeometry.overlaps(other)
+
+Returns true if the DE-9IM intersection matrix for the two geometries
+is ``T*T***T**`` (for two points or two surfaces) ``1*T***T**``
+(for two curves).
+
+.. method:: GEOSGeometry.relate_pattern(other, pattern)
+
+Returns ``True`` if the elements in the DE-9IM intersection matrix
+for this geometry and the other matches the given ``pattern`` --
+a string of nine characters from the alphabet: {``T``, ``F``, ``*``, ``0``}.
+
+.. method:: GEOSGeometry.touches(other)
+
+Returns ``True`` if the DE-9IM intersection matrix for the two geometries
+is ``FT*******``, ``F**T*****`` or ``F***T****``.
+
+.. method:: GEOSGeometry.within(other)
+
+Returns ``True`` if the DE-9IM intersection matrix for the two geometries
+is ``T*F**F***``.
+
+Topological Methods
+~~~~~~~~~~~~~~~~~~~
+
+.. method:: GEOSGeometry.buffer(width, quadsegs=8)
+
+Returns a :class:`GEOSGeometry` that represents all points whose distance
+from this geometry is less than or equal to the given ``width``. The optional
+``quadsegs`` keyword sets the number of segments used to approximate a
+quarter circle (defaults is 8).
+
+.. method:: GEOSGeometry.difference(other)
+
+Returns a :class:`GEOSGeometry` representing the points making up this
+geometry that do not make up other.
+
+.. method:: GEOSGeometry:intersection(other)
+
+Returns a :class:`GEOSGeometry` representing the points shared by this
+geometry and other.
+
+.. method:: GEOSGeometry.relate(other)
+
+Returns the DE-9IM intersection matrix (a string) representing the
+topological relationship between this geometry and the other.
+
+.. method:: GEOSGeometry.simplify(tolerance=0.0, preserve_topology=False)
+
+Returns a new :class:`GEOSGeometry`, simplified using the Douglas-Peucker
+algorithm to the specified tolerance. A higher tolerance value implies
+less points in the output. If no tolerance is tolerance provided,
+it defaults to 0.
+
+By default, this function does not preserve topology - e.g.,
+:class:`Polygon` objects can be split, collapsed into lines or disappear.
+:class:`Polygon` holes can be created or disappear, and lines can cross.
+By specifying ``preserve_topology=True``, the result will have the same
+dimension and number of components as the input, however, this is
+significantly slower.
+
+.. method:: GEOSGeometry.sym_difference(other)
+
+Returns a :class:`GEOSGeometry` combining the points in this geometry
+not in other, and the points in other not in this geometry.
+
+.. method:: GEOSGeometry.union(other)
+
+Returns a :class:`GEOSGeometry` representing all the points in this
+geometry and the other.
+
+Topological Properties
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. attribute:: GEOSGeometry.boundary
+
+Returns the boundary as a newly allocated Geometry object.
+
+.. attribute:: GEOSGeometry.centroid
+
+Returns a :class:`Point` object representing the geometric center of
+the geometry. The point is not guaranteed to be on the interior
+of the geometry.
+
+.. attribute:: GEOSGeometry.convex_hull
+
+Returns the smallest :class:`Polygon` that contains all the points in
+the geometry.
+
+.. attribute:: GEOSGeometry.envelope
+
+Returns a :class:`Polygon` that represents the bounding envelope of
+this geometry.
+
+.. attribute:: GEOSGeometry.point_on_surface
+
+Computes and returns a :class:`Point` guaranteed to be on the interior
+of this geometry.
+
+Other Properties & Methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. attribute:: GEOSGeometry.area
+
+This property returns the area of the Geometry.
+
+.. attribute:: GEOSGeometry.extent
+
+This property returns the extent of this geometry as a 4-tuple,
+consisting of (xmin, ymin, xmax, ymax).
+
+.. method:: GEOSGeometry.clone()
+
+This method returns a :class:`GEOSGeometry` that is a clone of the original.
+
+.. method:: GEOSGeometry.distance(geom)
+
+Returns the distance between the closest points on this geometry and the given
+``geom`` (another :class:`GEOSGeometry` object).
+
+.. note::
+
+ GEOS distance calculations are linear -- in other words, GEOS does not
+ perform a spherical calculation even if the SRID specifies a geographic
+ coordinate system.
+
+.. attribute:: GEOSGeometry.length
+
+Returns the length of this geometry (e.g., 0 for a :class:`Point`,
+the length of a :class:`LineString`, or the circumference of
+a :class:`Polygon`).
+
+.. attribute:: GEOSGeometry.prepared
+
+.. versionadded:: 1.1
+
+.. note::
+
+ Support for prepared geometries requires GEOS 3.1.
+
+Returns a GEOS ``PreparedGeometry`` for the contents of this geometry.
+``PreparedGeometry`` objects are optimized for the contains, intersects,
+and covers operations. Refer to the :ref:`prepared-geometries` documentation
+for more information.
+
+.. attribute:: GEOSGeometry.srs
+
+Returns a :class:`~django.contrib.gis.gdal.SpatialReference` object
+corresponding to the SRID of the geometry or ``None``.
+
+.. note::
+
+ Requires GDAL.
+
+.. method:: transform(ct, clone=False)
+
+Transforms the geometry according to the given coordinate transformation paramter
+(``ct``), which may be an integer SRID, spatial reference WKT string,
+a PROJ.4 string, a :class:`~django.contrib.gis.gdal.SpatialReference` object, or a
+:class:`~django.contrib.gis.gdal.CoordTransform` object. By default, the geometry
+is transformed in-place and nothing is returned. However if the ``clone`` keyword
+is set, then the geometry is not modified and a transformed clone of the geometry
+is returned instead.
+
+.. note::
+
+ Requires GDAL.
+
+``Point``
+---------
+
+.. class:: Point(x, y, z=None, srid=None)
+
+ ``Point`` objects are instantiated using arguments that represent
+ the component coordinates of the point or with a single sequence
+ coordinates. For example, the following are equivalent::
+
+ >>> pnt = Point(5, 23)
+ >>> pnt = Point([5, 23])
+
+``LineString``
+--------------
+
+.. class:: LineString(*args, **kwargs)
+
+ ``LineString`` objects are instantiated using arguments that are
+ either a sequence of coordinates or :class:`Point` objects.
+ For example, the following are equivalent::
+
+ >>> ls = LineString((0, 0), (1, 1))
+ >>> ls = LineString(Point(0, 0), Point(1, 1))
+
+ In addition, ``LineString`` objects may also be created by passing
+ in a single sequence of coordinate or :class:`Point` objects::
+
+ >>> ls = LineString( ((0, 0), (1, 1)) )
+ >>> ls = LineString( [Point(0, 0), Point(1, 1)] )
+
+``LinearRing``
+--------------
+
+.. class:: LinearRing(*args, **kwargs)
+
+ ``LinearRing`` objects are constructed in the exact same way as
+ :class:`LineString` objects, however the coordinates must be
+ *closed*, in other words, the first coordinates must be the
+ same as the last coordinates. For example::
+
+ >>> ls = LinearRing((0, 0), (0, 1), (1, 1), (0, 0))
+
+ Notice that ``(0, 0)`` is the first and last coordinate -- if
+ they were not equal, an error would be raised.
+
+``Polygon``
+-----------
+
+.. class:: Polygon(*args, **kwargs)
+
+ ``Polygon`` objects may be instantiated by passing in one or
+ more parameters that represent the rings of the polygon. The
+ parameters must either be :class:`LinearRing` instances, or
+ a sequence that may be used to construct a :class:`LinearRing`::
+
+ >>> ext_coords = ((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))
+ >>> int_coords = ((0.4, 0.4), (0.4, 0.6), (0.6, 0.6), (0.6, 0.4), (0.4, 0.4))
+ >>> poly = Polygon(ext_coords, int_coords)
+ >>> poly = Polygon(LinearRing(ext_coords), LinearRing(int_coords))
+
+ .. classmethod:: from_bbox(bbox)
+
+ .. versionadded:: 1.1
+
+ Returns a polygon object from the given bounding-box, a 4-tuple
+ comprising (xmin, ymin, xmax, ymax).
+
+ .. attribute:: num_interior_rings
+
+ Returns the number of interior rings in this geometry.
+
+Geometry Collections
+====================
+
+``MultiPoint``
+--------------
+
+.. class:: MultiPoint(*args, **kwargs)
+
+ ``MultiPoint`` objects may be instantiated by passing in one
+ or more :class:`Point` objects as arguments, or a single
+ sequence of :class:`Point` objects::
+
+ >>> mp = MultiPoint(Point(0, 0), Point(1, 1))
+ >>> mp = MultiPoint( (Point(0, 0), Point(1, 1)) )
+
+``MultiLineString``
+-------------------
+
+.. class:: MultiLineString(*args, **kwargs)
+
+ ``MultiLineString`` objects may be instantiated by passing in one
+ or more :class:`LineString` objects as arguments, or a single
+ sequence of :class:`LineString` objects::
+
+ >>> ls1 = LineString((0, 0), (1, 1))
+ >>> ls2 = LineString((2, 2), (3, 3))
+ >>> mls = MultiLineString(ls1, ls2)
+ >>> mls = MultiLineString([ls1, ls2])
+
+ .. attribute:: merged
+
+ .. versionadded:: 1.1
+
+ Returns a :class:`LineString` representing the line merge of
+ all the components in this ``MultiLineString``.
+
+
+``MultiPolygon``
+----------------
+
+.. class:: MultiPolygon(*args, **kwargs)
+
+ ``MultiPolygon`` objects may be instantiated by passing one or
+ more :class:`Polygon` objects as arguments, or a single sequence
+ of :class:`Polygon` objects::
+
+ >>> p1 = Polygon( ((0, 0), (0, 1), (1, 1), (0, 0)) )
+ >>> p2 = Polygon( ((1, 1), (1, 2), (2, 2), (1, 1)) )
+ >>> mp = MultiPolygon(p1, p2)
+ >>> mp = MultiPolygon([p1, p2])
+
+ .. attribute:: cascaded_union
+
+ .. versionadded:: 1.1
+
+ Returns a :class:`Polygon` that is the union of all of the component
+ polygons in this collection. The algorithm employed is significantly
+ more efficient (faster) than trying to union the geometries together
+ individually. [#fncascadedunion]_
+
+ .. note::
+
+ GEOS 3.1 is *required* to peform cascaded unions.
+
+``GeometryCollection``
+----------------------
+
+.. class:: GeometryCollection(*args, **kwargs)
+
+ ``GeometryCollection`` objects may be instantiated by passing in
+ one or more other :class:`GEOSGeometry` as arguments, or a single
+ sequence of :class:`GEOSGeometry` objects::
+
+ >>> poly = Polygon( ((0, 0), (0, 1), (1, 1), (0, 0)) )
+ >>> gc = GeometryCollection(Point(0, 0), MultiPoint(Point(0, 0), Point(1, 1)), poly)
+ >>> gc = GeometryCollection((Point(0, 0), MultiPoint(Point(0, 0), Point(1, 1)), poly))
+
+.. _prepared-geometries:
+
+Prepared Geometries
+===================
+
+.. versionadded: 1.1
+
+In order to obtain a prepared geometry, just access the
+:attr:`GEOSGeometry.prepared` property. Once you have a
+``PreparedGeometry`` instance its spatial predicate methods, listed below,
+may be used with other ``GEOSGeometry`` objects. An operation with a prepared
+geometry can be orders of magnitude faster -- the more complex the geometry
+that is prepared, the larger the speedup in the operation. For more information,
+please consult the `GEOS wiki page on prepared geometries <http://trac.osgeo.org/geos/wiki/PreparedGeometry>`_.
+
+.. note::
+
+ GEOS 3.1 is *required* in order to use prepared geometries.
+
+For example::
+
+ >>> from django.contrib.gis.geos import Point, Polygon
+ >>> poly = Polygon.from_bbox((0, 0, 5, 5))
+ >>> prep_poly = poly.prepared
+ >>> prep_poly.contains(Point(2.5, 2.5))
+ True
+
+``PreparedGeometry``
+--------------------
+
+.. class:: PreparedGeometry
+
+ All methods on ``PreparedGeometry`` take an ``other`` argument, which
+ must be a :class:`GEOSGeometry` instance.
+
+ .. method:: contains(other)
+
+ .. method:: contains_properly(other)
+
+ .. method:: covers(other)
+
+ .. method:: intersects(other)
+
+Geometry Factories
+==================
+
+.. function:: fromfile(file_h)
+
+ :param file_h: input file that contains spatial data
+ :type file_h: a Python ``file`` object or a string path to the file
+ :rtype: a :class:`GEOSGeometry` corresponding to the spatial data in the file
+
+Example::
+
+ >>> from django.contrib.gis.geos import fromfile
+ >>> g = fromfile('/home/bob/geom.wkt')
+
+.. function:: fromstr(string, [,srid=None])
+
+ :param string: string that contains spatial data
+ :type string: string
+ :param srid: spatial reference identifier
+ :type srid: integer
+ :rtype: a :class:`GEOSGeometry` corresponding to the spatial data in the string
+
+Example::
+
+ >>> from django.contrib.gis.geos import fromstr
+ >>> pnt = fromstr('POINT(-90.5 29.5)', srid=4326)
+
+I/O Objects
+===========
+
+.. versionadded: 1.1
+
+Reader Objects
+--------------
+
+The reader I/O classes simply return a :class:`GEOSGeometry` instance from the
+WKB and/or WKT input given to their ``read(geom)`` method.
+
+.. class:: WKBReader
+
+Example::
+
+ >>> from django.contrib.gis.geos import WKBReader
+ >>> wkb_r = WKBReader()
+ >>> wkb_r.read('0101000000000000000000F03F000000000000F03F')
+ <Point object at 0x103a88910>
+
+.. class:: WKTReader
+
+Example::
+
+ >>> from django.contrib.gis.geos import WKTReader
+ >>> wkt_r = WKTReader()
+ >>> wkt_r.read('POINT(1 1)')
+ <Point object at 0x103a88b50>
+
+Writer Objects
+--------------
+
+All writer objects have a ``write(geom)`` method that returns either the
+WKB or WKT of the given geometry. In addition, :class:`WKBWriter` objects
+also have properties that may be used to change the byte order, and or
+include the SRID and 3D values (in other words, EWKB).
+
+.. class:: WKBWriter
+
+``WKBWriter`` provides the most control over its output. By default it
+returns OGC-compliant WKB when it's ``write`` method is called. However,
+it has properties that allow for the creation of EWKB, a superset of the
+WKB standard that includes additional information.
+
+.. method:: WKBWriter.write(geom)
+
+Returns the WKB of the given geometry as a Python ``buffer`` object.
+Example::
+
+ >>> from django.contrib.gis.geos import Point, WKBWriter
+ >>> pnt = Point(1, 1)
+ >>> wkb_w = WKBWriter()
+ >>> wkb_w.write(pnt)
+ <read-only buffer for 0x103a898f0, size -1, offset 0 at 0x103a89930>
+
+.. method:: WKBWriter.write_hex(geom)
+
+Returns WKB of the geometry in hexadecimal. Example::
+
+ >>> from django.contrib.gis.geos import Point, WKBWriter
+ >>> pnt = Point(1, 1)
+ >>> wkb_w = WKBWriter()
+ >>> wkb_w.write_hex(pnt)
+ '0101000000000000000000F03F000000000000F03F'
+
+.. attribute:: WKBWriter.byteorder
+
+This property may be be set to change the byte-order of the geometry
+representation.
+
+=============== =================================================
+Byteorder Value Description
+=============== =================================================
+0 Big Endian (e.g., compatible with RISC systems)
+1 Little Endian (e.g., compatible with x86 systems)
+=============== =================================================
+
+Example::
+
+ >>> from django.contrib.gis.geos import Point, WKBWriter
+ >>> wkb_w = WKBWriter()
+ >>> pnt = Point(1, 1)
+ >>> wkb_w.write_hex(pnt)
+ '0101000000000000000000F03F000000000000F03F'
+ >>> wkb_w.byteorder = 0
+ '00000000013FF00000000000003FF0000000000000'
+
+.. attribute:: WKBWriter.outdim
+
+This property may be set to change the output dimension of the geometry
+representation. In other words, if you have a 3D geometry then set to 3
+so that the Z value is included in the WKB.
+
+============ ===========================
+Outdim Value Description
+============ ===========================
+2 The default, output 2D WKB.
+3 Output 3D EWKB.
+============ ===========================
+
+Example::
+
+ >>> from django.contrib.gis.geos import Point, WKBWriter
+ >>> wkb_w = WKBWriter()
+ >>> wkb_w.outdim
+ 2
+ >>> pnt = Point(1, 1, 1)
+ >>> wkb_w.write_hex(pnt) # By default, no Z value included:
+ '0101000000000000000000F03F000000000000F03F'
+ >>> wkb_w.outdim = 3 # Tell writer to include Z values
+ >>> wkb_w.write_hex(pnt)
+ '0101000080000000000000F03F000000000000F03F000000000000F03F'
+
+.. attribute:: WKBWriter.srid
+
+Set this property with a boolean to indicate whether the SRID of the
+geometry should be included with the WKB representation. Example::
+
+ >>> from django.contrib.gis.geos import Point, WKBWriter
+ >>> wkb_w = WKBWriter()
+ >>> pnt = Point(1, 1, srid=4326)
+ >>> wkb_w.write_hex(pnt) # By default, no SRID included:
+ '0101000000000000000000F03F000000000000F03F'
+ >>> wkb_w.srid = True # Tell writer to include SRID
+ >>> wkb_w.write_hex(pnt)
+ '0101000020E6100000000000000000F03F000000000000F03F'
+
+.. class:: WKTWriter
+
+.. method:: WKTWriter.write(geom)
+
+Returns the WKT of the given geometry. Example::
+
+ >>> from django.contrib.gis.geos import Point, WKTWriter
+ >>> pnt = Point(1, 1)
+ >>> wkt_w = WKTWriter()
+ >>> wkt_w.write(pnt)
+ 'POINT (1.0000000000000000 1.0000000000000000)'
+
+
+.. rubric:: Footnotes
+.. [#fnogc] *See* `PostGIS EWKB, EWKT and Canonical Forms <http://postgis.refractions.net/docs/ch04.html#id2591381>`_, PostGIS documentation at Ch. 4.1.2.
+.. [#fncascadedunion] For more information, read Paul Ramsey's blog post about `(Much) Faster Unions in PostGIS 1.4 <http://blog.cleverelephant.ca/2009/01/must-faster-unions-in-postgis-14.html>`_ and Martin Davis' blog post on `Fast polygon merging in JTS using Cascaded Union <http://lin-ear-th-inking.blogspot.com/2007/11/fast-polygon-merging-in-jts-using.html>`_.
+
+Settings
+========
+
+.. setting:: GEOS_LIBRARY_PATH
+
+GEOS_LIBRARY_PATH
+-----------------
+
+A string specifying the location of the GEOS C library. Typically,
+this setting is only used if the GEOS C library is in a non-standard
+location (e.g., ``/home/bob/lib/libgeos_c.so``).
+
+.. note::
+
+ The setting must be the *full* path to the **C** shared library; in
+ other words you want to use ``libgeos_c.so``, not ``libgeos.so``.
diff --git a/parts/django/docs/ref/contrib/gis/index.txt b/parts/django/docs/ref/contrib/gis/index.txt
new file mode 100644
index 0000000..c4959e0
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/index.txt
@@ -0,0 +1,33 @@
+.. _ref-contrib-gis:
+
+=========
+GeoDjango
+=========
+
+.. versionadded:: 1.0
+
+.. module:: django.contrib.gis
+ :synopsis: Geographic Information System (GIS) extensions for Django
+
+GeoDjango intends to be a world-class geographic Web framework. Its goal is to
+make it as easy as possible to build GIS Web applications and harness the power
+of spatially enabled data.
+
+.. toctree::
+ :maxdepth: 2
+
+ tutorial
+ install
+ model-api
+ db-api
+ geoquerysets
+ measure
+ geos
+ gdal
+ utils
+ commands
+ admin
+ feeds
+ sitemaps
+ testing
+ deployment
diff --git a/parts/django/docs/ref/contrib/gis/install.txt b/parts/django/docs/ref/contrib/gis/install.txt
new file mode 100644
index 0000000..fa8e34c
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/install.txt
@@ -0,0 +1,1190 @@
+.. _ref-gis-install:
+
+======================
+GeoDjango Installation
+======================
+
+Overview
+========
+In general, GeoDjango installation requires:
+
+1. :ref:`python24` and :ref:`django`
+2. :ref:`spatial_database`
+3. :ref:`geospatial_libs`
+
+Details for each of the requirements and installation instructions
+are provided in the sections below. In addition, platform-specific
+instructions are available for:
+
+* :ref:`macosx`
+* :ref:`ubuntudebian`
+* :ref:`windows`
+
+.. admonition:: Use the Source
+
+ Because GeoDjango takes advantage of the latest in the open source geospatial
+ software technology, recent versions of the libraries are necessary.
+ If binary packages aren't available for your platform,
+ :ref:`installation from source <build_from_source>`
+ may be required. When compiling the libraries from source, please follow the
+ directions closely, especially if you're a beginner.
+
+Requirements
+============
+
+.. _python24:
+
+Python 2.4+
+-----------
+
+Python 2.4 is the minimum version supported by Django, however Python 2.5+ is
+recommended because the `ctypes`__ module comes included; otherwise, 2.4 users
+will need to `download and install ctypes`__.
+
+__ http://docs.python.org/lib/module-ctypes.html
+__ http://sourceforge.net/projects/ctypes/files/
+
+.. _django:
+
+Django
+------
+
+Because GeoDjango is included with Django, please refer to Django's
+:doc:`installation instructions </intro/install>` for details on how to install.
+
+.. _spatial_database:
+
+Spatial Database
+----------------
+PostgreSQL (with PostGIS), MySQL, Oracle, and SQLite (with SpatiaLite) are
+the spatial databases currently supported.
+
+.. note::
+
+ PostGIS is recommended, because it is the most mature and feature-rich
+ open source spatial database.
+
+The geospatial libraries required for a GeoDjango installation depends
+on the spatial database used. The following lists the library requirements,
+supported versions, and any notes for each of the supported database backends:
+
+================== ============================== ================== ==========================================================
+Database Library Requirements Supported Versions Notes
+================== ============================== ================== ==========================================================
+PostgreSQL GEOS, PROJ.4, PostGIS 8.1+ Requires PostGIS.
+MySQL GEOS 5.x Not OGC-compliant; limited functionality.
+Oracle GEOS 10.2, 11 XE not supported; not tested with 9.
+SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 2.3+, pysqlite2 2.5+, and Django 1.1.
+================== ============================== ================== ==========================================================
+
+.. _geospatial_libs:
+
+Geospatial Libraries
+--------------------
+GeoDjango uses and/or provides interfaces for the the following open source
+geospatial libraries:
+
+======================== ==================================== ================================ ==========================
+Program Description Required Supported Versions
+======================== ==================================== ================================ ==========================
+:ref:`GEOS <ref-geos>` Geometry Engine Open Source Yes 3.2, 3.1, 3.0
+`PROJ.4`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 4.7, 4.6, 4.5, 4.4
+:ref:`GDAL <ref-gdal>` Geospatial Data Abstraction Library No (but, required for SQLite) 1.7, 1.6, 1.5, 1.4
+:ref:`GeoIP <ref-geoip>` IP-based geolocation library No 1.4
+`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 1.5, 1.4, 1.3
+`SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 2.4, 2.3
+======================== ==================================== ================================ ==========================
+
+.. admonition:: Install GDAL
+
+ While :ref:`gdalbuild` is technically not required, it is *recommended*.
+ Some features of GeoDjango (including the :ref:`ref-layermapping` and the geographic
+ admin) depend on its functionality.
+
+.. note::
+
+ The GeoDjango interfaces to GEOS, GDAL, and GeoIP may be used
+ independently of Django. In other words, no database or settings file
+ required -- just import them as normal from :mod:`django.contrib.gis`.
+
+.. _PROJ.4: http://trac.osgeo.org/proj/
+__ http://postgis.refractions.net/
+__ http://www.gaia-gis.it/spatialite/index.html
+
+.. _build_from_source:
+
+Building from Source
+====================
+
+When installing from source on UNIX and GNU/Linux systems, please follow
+the installation instructions carefully, and install the libraries in the
+given order. If using MySQL or Oracle as the spatial database, only GEOS
+is required.
+
+.. note::
+
+ On Linux platforms, it may be necessarry to run the ``ldconfig``
+ command after installing each library. For example::
+
+ $ sudo make install
+ $ sudo ldconfig
+
+.. note::
+
+ OS X users are required to install `Apple Developer Tools`_ in order
+ to compile software from source. This is typically included on your
+ OS X installation DVDs.
+
+.. _Apple Developer Tools: http://developer.apple.com/tools/xcode/
+
+.. _geosbuild:
+
+GEOS
+----
+
+GEOS is a C++ library for performing geometric operations, and is the default
+internal geometry representation used by GeoDjango (it's behind the "lazy"
+geometries). Specifically, the C API library is called (e.g., ``libgeos_c.so``)
+directly from Python using ctypes.
+
+First, download GEOS 3.2 from the refractions Web site and untar the source
+archive::
+
+ $ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2
+ $ tar xjf geos-3.2.2.tar.bz2
+
+Next, change into the directory where GEOS was unpacked, run the configure
+script, compile, and install::
+
+ $ cd geos-3.2.2
+ $ ./configure
+ $ make
+ $ sudo make install
+ $ cd ..
+
+Troubleshooting
+^^^^^^^^^^^^^^^
+
+Can't find GEOS Library
+~~~~~~~~~~~~~~~~~~~~~~~
+
+When GeoDjango can't find GEOS, this error is raised::
+
+ ImportError: Could not find the GEOS library (tried "geos_c"). Try setting GEOS_LIBRARY_PATH in your settings.
+
+The most common solution is to properly configure your :ref:`libsettings` *or* set
+:ref:`geoslibrarypath` in your settings.
+
+If using a binary package of GEOS (e.g., on Ubuntu 8.10), you may need to :ref:`binutils`.
+
+.. _geoslibrarypath:
+
+``GEOS_LIBRARY_PATH``
+~~~~~~~~~~~~~~~~~~~~~
+
+If your GEOS library is in a non-standard location, or you don't want to
+modify the system's library path then the :setting:`GEOS_LIBRARY_PATH` setting
+may be added to your Django settings file with the full path to the GEOS
+C library. For example::
+
+ GEOS_LIBRARY_PATH = '/home/bob/local/lib/libgeos_c.so'
+
+.. note::
+
+ The setting must be the *full* path to the **C** shared library; in
+ other words you want to use ``libgeos_c.so``, not ``libgeos.so``.
+
+.. _proj4:
+
+PROJ.4
+------
+
+`PROJ.4`_ is a library for converting geospatial data to different coordinate
+reference systems.
+
+First, download the PROJ.4 source code and datum shifting files [#]_::
+
+ $ wget http://download.osgeo.org/proj/proj-4.7.0.tar.gz
+ $ wget http://download.osgeo.org/proj/proj-datumgrid-1.5.zip
+
+Next, untar the source code archive, and extract the datum shifting files in the
+``nad`` subdirectory. This must be done *prior* to configuration::
+
+ $ tar xzf proj-4.7.0.tar.gz
+ $ cd proj-4.7.0/nad
+ $ unzip ../../proj-datumgrid-1.5.zip
+ $ cd ..
+
+Finally, configure, make and install PROJ.4::
+
+ $ ./configure
+ $ make
+ $ sudo make install
+ $ cd ..
+
+.. _postgis:
+
+PostGIS
+-------
+
+`PostGIS`__ adds geographic object support to PostgreSQL, turning it
+into a spatial database. :ref:`geosbuild` and :ref:`proj4` should be
+installed prior to building PostGIS.
+
+.. note::
+
+ The `psycopg2`_ module is required for use as the database adaptor
+ when using GeoDjango with PostGIS.
+
+.. _psycopg2: http://initd.org/projects/psycopg2
+
+First download the source archive, and extract::
+
+ $ wget http://postgis.refractions.net/download/postgis-1.5.2.tar.gz
+ $ tar xzf postgis-1.5.2.tar.gz
+ $ cd postgis-1.5.2
+
+Next, configure, make and install PostGIS::
+
+ $ ./configure
+
+Finally, make and install::
+
+ $ make
+ $ sudo make install
+ $ cd ..
+
+.. note::
+
+ GeoDjango does not automatically create a spatial database. Please
+ consult the section on :ref:`spatialdb_template` for more information.
+
+__ http://postgis.refractions.net/
+
+.. _gdalbuild:
+
+GDAL
+----
+
+`GDAL`__ is an excellent open source geospatial library that has support for
+reading most vector and raster spatial data formats. Currently, GeoDjango only
+supports :ref:`GDAL's vector data <ref-gdal>` capabilities [#]_.
+:ref:`geosbuild` and :ref:`proj4` should be installed prior to building GDAL.
+
+First download the latest GDAL release version and untar the archive::
+
+ $ wget http://download.osgeo.org/gdal/gdal-1.7.2.tar.gz
+ $ tar xzf gdal-1.7.2.tar.gz
+ $ cd gdal-1.7.2
+
+Configure, make and install::
+
+ $ ./configure
+ $ make # Go get some coffee, this takes a while.
+ $ sudo make install
+ $ cd ..
+
+.. note::
+
+ Because GeoDjango has it's own Python interface, the preceding instructions
+ do not build GDAL's own Python bindings. The bindings may be built by
+ adding the ``--with-python`` flag when running ``configure``. See
+ `GDAL/OGR In Python`__ for more information on GDAL's bindings.
+
+If you have any problems, please see the troubleshooting section below for
+suggestions and solutions.
+
+__ http://trac.osgeo.org/gdal/
+__ http://trac.osgeo.org/gdal/wiki/GdalOgrInPython
+
+.. _gdaltrouble:
+
+Troubleshooting
+^^^^^^^^^^^^^^^
+
+Can't find GDAL Library
+~~~~~~~~~~~~~~~~~~~~~~~
+
+When GeoDjango can't find the GDAL library, the ``HAS_GDAL`` flag
+will be false::
+
+ >>> from django.contrib.gis import gdal
+ >>> gdal.HAS_GDAL
+ False
+
+The solution is to properly configure your :ref:`libsettings` *or* set
+:ref:`gdallibrarypath` in your settings.
+
+.. _gdallibrarypath:
+
+``GDAL_LIBRARY_PATH``
+~~~~~~~~~~~~~~~~~~~~~
+
+If your GDAL library is in a non-standard location, or you don't want to
+modify the system's library path then the :setting:`GDAL_LIBRARY_PATH`
+setting may be added to your Django settings file with the full path to
+the GDAL library. For example::
+
+ GDAL_LIBRARY_PATH = '/home/sue/local/lib/libgdal.so'
+
+.. _gdaldata:
+
+Can't find GDAL data files (``GDAL_DATA``)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When installed from source, GDAL versions 1.5.1 and below have an autoconf bug
+that places data in the wrong location. [#]_ This can lead to error messages
+like this::
+
+ ERROR 4: Unable to open EPSG support file gcs.csv.
+ ...
+ OGRException: OGR failure.
+
+The solution is to set the ``GDAL_DATA`` environment variable to the location of the
+GDAL data files before invoking Python (typically ``/usr/local/share``; use
+``gdal-config --datadir`` to find out). For example::
+
+ $ export GDAL_DATA=`gdal-config --datadir`
+ $ python manage.py shell
+
+If using Apache, you may need to add this environment variable to your configuration
+file::
+
+ SetEnv GDAL_DATA /usr/local/share
+
+.. _spatialite:
+
+SpatiaLite
+----------
+.. versionadded:: 1.1
+
+.. note::
+
+ Mac OS X users should follow the instructions in the :ref:`kyngchaos` section,
+ as it is much easier than building from source.
+
+`SpatiaLite`__ adds spatial support to SQLite, turning it into a full-featured
+spatial database. Because SpatiaLite has special requirements, it typically
+requires SQLite and pysqlite2 (the Python SQLite DB-API adaptor) to be built from
+source. :ref:`geosbuild` and :ref:`proj4` should be installed prior to building
+SpatiaLite.
+
+After installation is complete, don't forget to read the post-installation
+docs on :ref:`create_spatialite_db`.
+
+__ http://www.gaia-gis.it/spatialite/index.html
+
+.. _sqlite:
+
+SQLite
+^^^^^^
+
+Typically, SQLite packages are not compiled to include the `R*Tree module`__ --
+thus it must be compiled from source. First download the latest amalgamation
+source archive from the `SQLite download page`__, and extract::
+
+ $ wget http://sqlite.org/sqlite-amalgamation-3.6.23.1.tar.gz
+ $ tar xzf sqlite-amalgamation-3.6.23.1.tar.gz
+ $ cd sqlite-3.6.23.1
+
+Next, run the ``configure`` script -- however the ``CFLAGS`` environment variable
+needs to be customized so that SQLite knows to build the R*Tree module::
+
+ $ CFLAGS="-DSQLITE_ENABLE_RTREE=1" ./configure
+ $ make
+ $ sudo make install
+ $ cd ..
+
+.. note::
+
+ If using Ubuntu, installing a newer SQLite from source can be very difficult
+ because it links to the existing ``libsqlite3.so`` in ``/usr/lib`` which
+ many other packages depend on. Unfortunately, the best solution at this time
+ is to overwrite the existing library by adding ``--prefix=/usr`` to the
+ ``configure`` command.
+
+__ http://www.sqlite.org/rtree.html
+__ http://www.sqlite.org/download.html
+
+.. _spatialitebuild :
+
+SpatiaLite Library (``libspatialite``) and Tools (``spatialite``)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+After SQLite has been built with the R*Tree module enabled, get the latest
+SpatiaLite library source and tools bundle from the `download page`__::
+
+ $ wget http://www.gaia-gis.it/spatialite/libspatialite-amalgamation-2.3.1.tar.gz
+ $ wget http://www.gaia-gis.it/spatialite/spatialite-tools-2.3.1.tar.gz
+ $ tar xzf libspatialite-amalgamation-2.3.1.tar.gz
+ $ tar xzf spatialite-tools-2.3.1.tar.gz
+
+Prior to attempting to build, please read the important notes below to see if
+customization of the ``configure`` command is necessary. If not, then run the
+``configure`` script, make, and install for the SpatiaLite library::
+
+ $ cd libspatialite-amalgamation-2.3.1
+ $ ./configure # May need to modified, see notes below.
+ $ make
+ $ sudo make install
+ $ cd ..
+
+Finally, do the same for the SpatiaLite tools::
+
+ $ cd spatialite-tools-2.3.1
+ $ ./configure # May need to modified, see notes below.
+ $ make
+ $ sudo make install
+ $ cd ..
+
+.. note::
+
+ If you've installed GEOS and PROJ.4 from binary packages, you will have to specify
+ their paths when running the ``configure`` scripts for *both* the library and the
+ tools (the configure scripts look, by default, in ``/usr/local``). For example,
+ on Debian/Ubuntu distributions that have GEOS and PROJ.4 packages, the command would be::
+
+ $ ./configure --with-proj-include=/usr/include --with-proj-lib=/usr/lib --with-geos-include=/usr/include --with-geos-lib=/usr/lib
+
+.. note::
+
+ For Mac OS X users building from source, the SpatiaLite library *and* tools
+ need to have their ``target`` configured::
+
+ $ ./configure --target=macosx
+
+__ http://www.gaia-gis.it/spatialite/sources.html
+
+.. _pysqlite2:
+
+pysqlite2
+^^^^^^^^^
+
+Because SpatiaLite must be loaded as an external extension, it requires the
+``enable_load_extension`` method, which is only available in versions 2.5+.
+Thus, download pysqlite2 2.6, and untar::
+
+ $ wget http://pysqlite.googlecode.com/files/pysqlite-2.6.0.tar.gz
+ $ tar xzf pysqlite-2.6.0.tar.gz
+ $ cd pysqlite-2.6.0
+
+Next, use a text editor (e.g., ``emacs`` or ``vi``) to edit the ``setup.cfg`` file
+to look like the following::
+
+ [build_ext]
+ #define=
+ include_dirs=/usr/local/include
+ library_dirs=/usr/local/lib
+ libraries=sqlite3
+ #define=SQLITE_OMIT_LOAD_EXTENSION
+
+.. note::
+
+ The important thing here is to make sure you comment out the the
+ ``define=SQLITE_OMIT_LOAD_EXTENSION`` flag and that the ``include_dirs``
+ and ``library_dirs`` settings are uncommented and set to the appropriate
+ path if the SQLite header files and libraries are not in ``/usr/include``
+ and ``/usr/lib``, respectively.
+
+After modifying ``setup.cfg`` appropriately, then run the ``setup.py`` script
+to build and install::
+
+ $ sudo python setup.py install
+
+Post-Installation
+=================
+
+.. _spatialdb_template:
+
+Creating a Spatial Database Template for PostGIS
+------------------------------------------------
+
+Creating a spatial database with PostGIS is different than normal because
+additional SQL must be loaded to enable spatial functionality. Because of
+the steps in this process, it's better to create a database template that
+can be reused later.
+
+First, you need to be able to execute the commands as a privileged database
+user. For example, you can use the following to become the ``postgres`` user::
+
+ $ sudo su - postgres
+
+.. note::
+
+ The location *and* name of the PostGIS SQL files (e.g., from
+ ``POSTGIS_SQL_PATH`` below) depends on the version of PostGIS.
+ PostGIS versions 1.3 and below use ``<pg_sharedir>/contrib/lwpostgis.sql``;
+ whereas version 1.4 uses ``<sharedir>/contrib/postgis.sql`` and
+ version 1.5 uses ``<sharedir>/contrib/postgis-1.5/postgis.sql``.
+
+ The example below assumes PostGIS 1.5, thus you may need to modify
+ ``POSTGIS_SQL_PATH`` and the name of the SQL file for the specific
+ version of PostGIS you are using.
+
+Once you're a database super user, then you may execute the following commands
+to create a PostGIS spatial database template. If running Ubuntu :ref:`ibex`
+or Debian :ref:`lenny`, please refer to their specific documentation for
+modifications to these commands::
+
+ $ POSTGIS_SQL_PATH=`pg_config --sharedir`/contrib/postgis-1.5
+ # Creating the template spatial database.
+ $ createdb -E UTF8 template_postgis
+ $ createlang -d template_postgis plpgsql # Adding PLPGSQL language support.
+ # Allows non-superusers the ability to create from this template
+ $ psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
+ # Loading the PostGIS SQL routines
+ $ psql -d template_postgis -f $POSTGIS_SQL_PATH/postgis.sql
+ $ psql -d template_postgis -f $POSTGIS_SQL_PATH/spatial_ref_sys.sql
+ # Enabling users to alter spatial tables.
+ $ psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;"
+ $ psql -d template_postgis -c "GRANT ALL ON geography_columns TO PUBLIC;"
+ $ psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
+
+These commands may be placed in a shell script for later use; for convenience
+the following scripts are available:
+
+=============== ==========================================
+PostGIS Version Shell Script
+=============== ==========================================
+1.3 `create_template_postgis-1.3.sh`_
+1.4 `create_template_postgis-1.4.sh`_
+1.5 `create_template_postgis-1.5.sh`_
+=============== ==========================================
+
+Afterwards, you may create a spatial database by simply specifying
+``template_postgis`` as the template to use (via the ``-T`` option)::
+
+ $ createdb -T template_postgis <db name>
+
+.. note::
+
+ While the ``createdb`` command does not require database super-user privileges,
+ it must be executed by a database user that has permissions to create databases.
+ You can create such a user with the following command::
+
+ $ createuser --createdb <user>
+
+.. _create_template_postgis-1.3.sh: http://geodjango.org/docs/create_template_postgis-1.3.sh
+.. _create_template_postgis-1.4.sh: http://geodjango.org/docs/create_template_postgis-1.4.sh
+.. _create_template_postgis-1.5.sh: http://geodjango.org/docs/create_template_postgis-1.5.sh
+.. _create_template_postgis-debian.sh: http://geodjango.org/docs/create_template_postgis-debian.sh
+
+.. _create_spatialite_db:
+
+Creating a Spatial Database for SpatiaLite
+-------------------------------------------
+
+After the SpatiaLite library and tools have been installed, it is now possible
+to create spatial database for use with GeoDjango. In order to do this, download
+the spatial database initialization SQL from the `SpatiaLite Resources`__ page::
+
+ $ wget http://www.gaia-gis.it/spatialite/init_spatialite-2.3.sql.gz
+ $ gunzip init_spatialite-2.3.sql.gz
+
+Now, the ``spatialite`` command can be used to initialize a spatial database::
+
+ $ spatialite geodjango.db < init_spatialite-2.3.sql
+
+.. note::
+
+ The parameter ``geodjango.db`` is the *filename* of the SQLite database
+ you want to use. Use the same in the :setting:`DATABASE_NAME`
+ inside your ``settings.py``.
+
+
+__ http://www.gaia-gis.it/spatialite/resources.html
+
+
+Add ``django.contrib.gis`` to ``INSTALLED_APPS``
+------------------------------------------------
+
+Like other Django contrib applications, you will *only* need to add
+:mod:`django.contrib.gis` to :setting:`INSTALLED_APPS` in your settings.
+This is the so that ``gis`` templates can be located -- if not done, then
+features such as the geographic admin or KML sitemaps will not function properly.
+
+.. _addgoogleprojection:
+
+Add Google Projection to ``spatial_ref_sys`` table
+--------------------------------------------------
+
+.. versionchanged:: 1.2
+
+.. note::
+
+ If running PostGIS 1.4 and above, the entry is already included in the
+ default ``spatial_ref_sys`` table. You can skip this step.
+
+In order to conduct database transformations to the so-called "Google"
+projection (a spherical mercator projection used by Google Maps),
+an entry must be added to your spatial database's ``spatial_ref_sys`` table.
+Invoke the Django shell from your project and execute the
+``add_srs_entry`` function::
+
+ $ python manage shell
+ >>> from django.contrib.gis.utils import add_srs_entry
+ >>> add_srs_entry(900913)
+
+.. note::
+
+ In Django 1.1 the name of this function is ``add_postgis_srs``.
+
+This adds an entry for the 900913 SRID to the ``spatial_ref_sys`` (or equivalent)
+table, making it possible for the spatial database to transform coordinates in
+this projection. You only need to execute this command *once* per spatial database.
+
+Troubleshooting
+===============
+
+If you can't find the solution to your problem here then participate in the
+community! You can:
+
+* Join the ``#geodjango`` IRC channel on FreeNode (may be accessed on the
+ Web via `Mibbit`__). Please be patient and polite -- while you may not
+ get an immediate response, someone will attempt to answer your question
+ as soon as they see it.
+* Ask your question on the `GeoDjango`__ mailing list.
+* File a ticket on the `Django trac`__ if you think there's a bug. Make
+ sure to provide a complete description of the problem, versions used,
+ and specify the component as "GIS".
+
+__ http://www.mibbit.com/?server=irc.freenode.net&channel=%23geodjango
+__ http://groups.google.com/group/geodjango
+__ http://code.djangoproject.com/simpleticket
+
+.. _libsettings:
+
+Library Environment Settings
+----------------------------
+
+By far, the most common problem when installing GeoDjango is that the
+external shared libraries (e.g., for GEOS and GDAL) cannot be located. [#]_
+Typically, the cause of this problem is that the operating system isn't aware
+of the directory where the libraries built from source were installed.
+
+In general, the library path may be set on a per-user basis by setting
+an environment variable, or by configuring the library path for the entire
+system.
+
+``LD_LIBRARY_PATH`` environment variable
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A user may set this environment variable to customize the library paths
+they want to use. The typical library directory for software
+built from source is ``/usr/local/lib``. Thus, ``/usr/local/lib`` needs
+to be included in the ``LD_LIBRARY_PATH`` variable. For example, the user
+could place the following in their bash profile::
+
+ export LD_LIBRARY_PATH=/usr/local/lib
+
+Setting System Library Path
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+On GNU/Linux systems, there is typically a file in ``/etc/ld.so.conf``, which may include
+additional paths from files in another directory, such as ``/etc/ld.so.conf.d``.
+As the root user, add the custom library path (like ``/usr/local/lib``) on a
+new line in ``ld.so.conf``. This is *one* example of how to do so::
+
+ $ sudo echo /usr/local/lib >> /etc/ld.so.conf
+ $ sudo ldconfig
+
+For OpenSolaris users, the system library path may be modified using the
+``crle`` utility. Run ``crle`` with no options to see the current configuration
+and use ``crle -l`` to set with the new library path. Be *very* careful when
+modifying the system library path::
+
+ # crle -l $OLD_PATH:/usr/local/lib
+
+.. _binutils:
+
+Install ``binutils``
+^^^^^^^^^^^^^^^^^^^^
+
+GeoDjango uses the ``find_library`` function (from the ``ctypes.util`` Python
+module) to discover libraries. The ``find_library`` routine uses a program
+called ``objdump`` (part of the ``binutils`` package) to verify a shared
+library on GNU/Linux systems. Thus, if ``binutils`` is not installed on your
+Linux system then Python's ctypes may not be able to find your library even if
+your library path is set correctly and geospatial libraries were built perfectly.
+
+The ``binutils`` package may be installed on Debian and Ubuntu systems using the
+following command::
+
+ $ sudo apt-get install binutils
+
+Similarly, on Red Hat and CentOS systems::
+
+ $ sudo yum install binutils
+
+Platform Specific Instructions
+==============================
+
+.. _macosx:
+
+Mac OS X
+--------
+
+Because of the variety of packaging systems available for OS X, users have
+several different options for installing GeoDjango. These options are:
+
+* :ref:`kyngchaos`
+* :ref:`fink`
+* :ref:`macports`
+* :ref:`build_from_source`
+
+.. note::
+
+ Currently, the easiest and recommended approach for installing GeoDjango
+ on OS X is to use the KyngChaos packages.
+
+This section also includes instructions for installing an upgraded version
+of :ref:`macosx_python` from packages provided by the Python Software
+Foundation, however, this is not required.
+
+.. _macosx_python:
+
+Python
+^^^^^^
+
+Although OS X comes with Python installed, users can use framework
+installers (`2.5`__ and `2.6`__ are available) provided by
+the Python Software Foundation. An advantage to using the installer is
+that OS X's Python will remain "pristine" for internal operating system
+use.
+
+__ http://python.org/ftp/python/2.5.4/python-2.5.4-macosx.dmg
+__ http://python.org/ftp/python/2.6.2/python-2.6.2-macosx2009-04-16.dmg
+
+.. note::
+
+ You will need to modify the ``PATH`` environment variable in your
+ ``.profile`` file so that the new version of Python is used when
+ ``python`` is entered at the command-line::
+
+ export PATH=/Library/Frameworks/Python.framework/Versions/Current/bin:$PATH
+
+.. _kyngchaos:
+
+KyngChaos Packages
+^^^^^^^^^^^^^^^^^^
+
+William Kyngesburye provides a number of `geospatial library binary packages`__
+that make it simple to get GeoDjango installed on OS X without compiling
+them from source. However, the `Apple Developer Tools`_ are still necessary
+for compiling the Python database adapters :ref:`psycopg2_kyngchaos` (for PostGIS)
+and :ref:`pysqlite2_kyngchaos` (for SpatiaLite).
+
+.. note::
+
+ SpatiaLite users should consult the :ref:`spatialite_kyngchaos` section
+ after installing the packages for additional instructions.
+
+Download the framework packages for:
+
+* UnixImageIO
+* PROJ
+* GEOS
+* SQLite3 (includes the SpatiaLite library)
+* GDAL
+
+Install the packages in the order they are listed above, as the GDAL and SQLite
+packages require the packages listed before them. Afterwards, you can also
+install the KyngChaos binary packages for `PostgreSQL and PostGIS`__.
+
+After installing the binary packages, you'll want to add the following to
+your ``.profile`` to be able to run the package programs from the command-line::
+
+ export PATH=/Library/Frameworks/UnixImageIO.framework/Programs:$PATH
+ export PATH=/Library/Frameworks/PROJ.framework/Programs:$PATH
+ export PATH=/Library/Frameworks/GEOS.framework/Programs:$PATH
+ export PATH=/Library/Frameworks/SQLite3.framework/Programs:$PATH
+ export PATH=/Library/Frameworks/GDAL.framework/Programs:$PATH
+ export PATH=/usr/local/pgsql/bin:$PATH
+
+__ http://www.kyngchaos.com/software/frameworks
+__ http://www.kyngchaos.com/software/postgres
+
+.. note::
+
+ Use of these binaries requires Django 1.0.3 and above. If you are
+ using a previous version of Django (like 1.0.2), then you will have
+ to add the the following in your settings::
+
+ GEOS_LIBRARY_PATH='/Library/Frameworks/GEOS.framework/GEOS'
+ GDAL_LIBRARY_PATH='/Library/Frameworks/GDAL.framework/GDAL'
+
+.. _psycopg2_kyngchaos:
+
+psycopg2
+~~~~~~~~
+
+After you've installed the KyngChaos binaries and modified your ``PATH``, as
+described above, ``psycopg2`` may be installed using the following command::
+
+ $ sudo python easy_install psycopg2
+
+.. note::
+
+ To use ``easy_install`` you'll need to install Python's `setuptools`_.
+
+.. _setuptools: http://pypi.python.org/pypi/setuptools
+
+.. _pysqlite2_kyngchaos:
+
+pysqlite2
+~~~~~~~~~
+
+Follow the :ref:`pysqlite2` source install instructions, however,
+when editing the ``setup.cfg`` use the following instead::
+
+ [build_ext]
+ #define=
+ include_dirs=/Library/Frameworks/SQLite3.framework/unix/include
+ library_dirs=/Library/Frameworks/SQLite3.framework/unix/lib
+ libraries=sqlite3
+ #define=SQLITE_OMIT_LOAD_EXTENSION
+
+.. _spatialite_kyngchaos:
+
+SpatiaLite
+~~~~~~~~~~
+
+When :ref:`create_spatialite_db`, the ``spatialite`` program is required.
+However, instead of attempting to compile the SpatiaLite tools from source,
+download the `SpatiaLite Binaries`__ for OS X, and install ``spatialite`` in a
+location available in your ``PATH``. For example::
+
+ $ curl -O http://www.gaia-gis.it/spatialite/spatialite-tools-osx-x86-2.3.1.tar.gz
+ $ tar xzf spatialite-tools-osx-x86-2.3.1.tar.gz
+ $ cd spatialite-tools-osx-x86-2.3.1/bin
+ $ sudo cp spatialite /Library/Frameworks/SQLite3.framework/Programs
+
+Finally, for GeoDjango to be able to find the KyngChaos SpatiaLite library,
+add the following to your ``settings.py``::
+
+ SPATIALITE_LIBRARY_PATH='/Library/Frameworks/SQLite3.framework/SQLite3'
+
+__ http://www.gaia-gis.it/spatialite/binaries.html
+
+.. _fink:
+
+Fink
+^^^^
+
+`Kurt Schwehr`__ has been gracious enough to create GeoDjango packages for users
+of the `Fink`__ package system. The following packages are available, depending
+on which version of Python you want to use:
+
+* ``django-gis-py26``
+* ``django-gis-py25``
+* ``django-gis-py24``
+
+__ http://schwehr.org/blog/
+__ http://www.finkproject.org/
+
+.. _macports:
+
+MacPorts
+^^^^^^^^
+
+`MacPorts`__ may be used to install GeoDjango prerequisites on Macintosh
+computers running OS X. Because MacPorts still builds the software from source,
+the `Apple Developer Tools`_ are required.
+
+Summary::
+
+ $ sudo port install postgresql83-server
+ $ sudo port install geos
+ $ sudo port install proj
+ $ sudo port install postgis
+ $ sudo port install gdal
+ $ sudo port install libgeoip
+
+.. note::
+
+ You will also have to modify the ``PATH`` in your ``.profile`` so
+ that the MacPorts programs are accessible from the command-line::
+
+ export PATH=/opt/local/bin:/opt/local/lib/postgresql83/bin
+
+ In addition, add the ``FALLBACK_DYLD_LIBRARY_PATH`` setting so that
+ the libraries can be found by Python::
+
+ export FALLBACK_DYLD_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql83
+
+__ http://www.macports.org/
+
+.. _ubuntudebian:
+
+Ubuntu & Debian GNU/Linux
+-------------------------
+
+.. _ubuntu:
+
+Ubuntu
+^^^^^^
+
+.. _heron:
+
+8.04 and lower
+~~~~~~~~~~~~~~
+
+The 8.04 (and lower) versions of Ubuntu use GEOS v2.2.3 in their binary packages,
+which is incompatible with GeoDjango. Thus, do *not* use the binary packages
+for GEOS or PostGIS and build some prerequisites from source, per the instructions
+in this document; however, it is okay to use the PostgreSQL binary packages.
+
+For more details, please see the Debian instructions for :ref:`etch` below.
+
+.. _ibex:
+
+8.10
+~~~~
+
+Use the synaptic package manager to install the following packages::
+
+ $ sudo apt-get install binutils libgdal1-1.5.0 postgresql-8.3-postgis postgresql-server-dev-8.3 python-psycopg2 python-setuptools
+
+Afterwards, you may install Django with Python's ``easy_install`` script (the
+Ubuntu package ``python-django`` uses an older version missing several
+important bug fixes for GeoDjango)::
+
+ $ sudo easy_install Django
+
+That's it! For the curious, the required binary prerequisites packages are:
+
+* ``binutils``: for ctypes to find libraries
+* ``postgresql-8.3``
+* ``postgresql-server-dev-8.3``: for ``pg_config``
+* ``postgresql-8.3-postgis``: for PostGIS 1.3.3
+* ``libgeos-3.0.0``, and ``libgeos-c1``: for GEOS 3.0.0
+* ``libgdal1-1.5.0``: for GDAL 1.5.0 library
+* ``proj``: for PROJ 4.6.0 -- but no datum shifting files, see note below
+* ``python-psycopg2``
+* ``python-setuptools``: for ``easy_install``
+
+Optional packages to consider:
+
+* ``libgeoip1``: for :ref:`GeoIP <ref-geoip>` support
+* ``gdal-bin``: for GDAL command line programs like ``ogr2ogr``
+* ``python-gdal`` for GDAL's own Python bindings -- includes interfaces for raster manipulation
+
+.. note::
+
+ The Ubuntu ``proj`` package does not come with the datum shifting files
+ installed, which will cause problems with the geographic admin because
+ the ``null`` datum grid is not available for transforming geometries to the
+ spherical mercator projection. A solution is to download the
+ datum-shifting files, create the grid file, and install it yourself::
+
+ $ wget http://download.osgeo.org/proj/proj-datumgrid-1.4.tar.gz
+ $ mkdir nad
+ $ cd nad
+ $ tar xzf ../proj-datumgrid-1.4.tar.gz
+ $ nad2bin null < null.lla
+ $ sudo cp null /usr/share/proj
+
+ Otherwise, the Ubuntu ``proj`` package is fine for general use as long as you
+ do not plan on doing any database transformation of geometries to the
+ Google projection (900913).
+
+.. note::
+
+ The PostGIS SQL files are not placed the PostgreSQL share directory in the
+ Ubuntu packages. Use the `create_template_postgis-debian.sh`_ script
+ instead when :ref:`spatialdb_template`.
+
+.. _debian:
+
+Debian
+------
+
+.. _etch:
+
+4.0 (Etch)
+^^^^^^^^^^
+The situation here is the same as that of Ubuntu :ref:`heron` -- in other words,
+some packages must be built from source to work properly with GeoDjango.
+
+Binary Packages
+~~~~~~~~~~~~~~~
+The following command will install acceptable binary packages, as well as
+the development tools necessary to build the rest of the requirements::
+
+ $ sudo apt-get install binutils bzip2 gcc g++ flex make postgresql-8.1 postgresql-server-dev-8.1 python-ctypes python-psycopg2 python-setuptools
+
+Required package information:
+
+* ``binutils``: for ctypes to find libraries
+* ``bzip2``: for decompressing the source packages
+* ``gcc``, ``g++``, ``make``: GNU developer tools used to compile the libraries
+* ``flex``: required to build PostGIS
+* ``postgresql-8.1``
+* ``postgresql-server-dev-8.1``: for ``pg_config``
+* ``python-ctypes``: Python 2.4 needs to have ctypes installed separately
+* ``python-psycopg2``
+* ``python-setuptools``: for ``easy_install``
+
+Optional packages:
+
+* ``libgeoip``: for :ref:`GeoIP <ref-geoip>` support
+
+Source Packages
+~~~~~~~~~~~~~~~
+You will still have to install :ref:`geosbuild`, :ref:`proj4`,
+:ref:`postgis`, and :ref:`gdalbuild` from source. Please follow the
+directions carefully.
+
+.. _lenny:
+
+5.0 (Lenny)
+^^^^^^^^^^^
+This version is comparable to Ubuntu :ref:`ibex`, so the command
+is very similar::
+
+ $ sudo apt-get install binutils libgdal1-1.5.0 postgresql-8.3 postgresql-8.3-postgis postgresql-server-dev-8.3 python-psycopg2 python-setuptools
+
+This assumes that you are using PostgreSQL version 8.3. Else, replace ``8.3``
+in the above command with the appropriate PostgreSQL version.
+
+.. note::
+
+ Please read the note in the Ubuntu :ref:`ibex` install documentation
+ about the ``proj`` package -- it also applies here because the package does
+ not include the datum shifting files.
+
+.. _post_install:
+
+Post-installation Notes
+~~~~~~~~~~~~~~~~~~~~~~~
+
+If the PostgreSQL database cluster was not initiated after installing, then it
+can be created (and started) with the following command::
+
+ $ sudo pg_createcluster --start 8.3 main
+
+Afterwards, the ``/etc/init.d/postgresql-8.3`` script should be used to manage
+the starting and stopping of PostgreSQL.
+
+In addition, the SQL files for PostGIS are placed in a different location on
+Debian 5.0 . Thus when :ref:`spatialdb_template` either:
+
+* Create a symbolic link to these files::
+
+ $ sudo ln -s /usr/share/postgresql-8.3-postgis/{lwpostgis,spatial_ref_sys}.sql /usr/share/postgresql/8.3
+
+ If not running PostgreSQL 8.3, then replace ``8.3`` in the command above with the correct version.
+
+* Or use the `create_template_postgis-debian.sh`_ to create the spatial database.
+
+.. _windows:
+
+Windows XP
+----------
+
+Python
+^^^^^^
+
+First, download the `Python 2.6 installer`__ from the Python Web site. Next,
+execute the installer and use defaults, e.g., keep 'Install for all users'
+checked and the installation path set as ``C:\Python26``.
+
+.. note::
+
+ You may already have a version of Python installed in ``C:\python`` as ESRI
+ products sometimes install a copy there. *You should still install a
+ fresh version of Python 2.6.*
+
+__ http://python.org/ftp/python/2.6.2/python-2.6.2.msi
+
+PostgreSQL
+^^^^^^^^^^
+
+First, select a mirror and download the latest `PostgreSQL 8.3 installer`__ from
+the EnterpriseDB Web site.
+
+.. note::
+
+ PostgreSQL 8.3 is required because PostGIS is not available yet for 8.4.
+
+After downloading, simply click on the installer, follow the
+on-screen directions, and keep the default options (e.g., keep the installation
+path as ``C:\Program Files\PostgreSQL\8.3``).
+
+.. note::
+
+ This PostgreSQL installation process will create both a new windows user to be the
+ 'postgres service account' and a special 'postgres superuser' to own the database
+ cluster. You will be prompted to set a password for both users (make sure to write
+ them down!). To see basic details on the 'service user' account right click on
+ 'My Computer' and select 'Manage' or go to: Control Panel -> Administrative Tools ->
+ Computer Management -> System Tools -> Local Users and Groups.
+
+If installed successfully, the PostgreSQL server will run in the background each time
+the system as started as a Windows service. When finished, the installer should launch
+the Application Stack Builder (ASB) -- use this to install PostGIS, see instructions
+below for more details. A 'PostgreSQL 8.3' start menu group should be created that
+contains shortcuts for the ASB and 'Command Prompt', which launches a terminal window
+in the PostgreSQL directory.
+
+__ http://www.enterprisedb.com/products/pgdownload.do#windows
+
+PostGIS
+^^^^^^^
+
+From the Application Stack Builder (Programs -> PostgreSQL 8.3), select
+'PostgreSQL Database Server 8.3 on port 5432' from the drop down menu. Next,
+select 'PostGIS 1.3.6 for PostgreSQL 8.3' from the 'Spatial Extensions' tree
+in the list. Select only the default options during install (do not uncheck
+the option to create a default PostGIS database).
+
+.. note::
+
+ You will be prompted to enter your 'postgres superuser' password in the
+ 'Database Connection Information' dialog.
+
+psycopg2
+^^^^^^^^
+
+The ``psycopg2`` Python module provides the interface between Python and the
+PostgreSQL database. Download the `Windows installer`__ (v2.0.10) and run
+using the default settings. [#]_
+
+__ http://www.stickpeople.com/projects/python/win-psycopg/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.exe
+
+GeoDjango Installer
+^^^^^^^^^^^^^^^^^^^
+
+Download the `GeoDjango Installer`__; this was created [#]_ to simplify the rest
+of the process for installing GeoDjango on Windows platforms. The installer
+automatically installs Django 1.1, GDAL 1.6.0, PROJ 4.6.1 (including datum grid
+files), and configures the necessary environment variables.
+
+Once the installer has completed, log out and log back in so that the
+modifications to the system environment variables take effect, and you
+should be good to go.
+
+.. note::
+
+ The installer modifies the system ``Path`` environment variable to
+ include ``C:\Program Files\PostgreSQL\8.3\bin`` and
+ ``C:\Program Files\GeoDjango\bin``. This is required so that Python
+ may find the GEOS DLL provided by PostGIS and the GDAL DLL provided
+ by the installer. The installer also sets the ``GDAL_DATA`` and
+ ``PROJ_LIB`` environment variables.
+
+__ http://geodjango.org/windows/GeoDjango_Installer.exe
+
+.. rubric:: Footnotes
+.. [#] The datum shifting files are needed for converting data to and from certain projections.
+ For example, the PROJ.4 string for the `Google projection (900913) <http://spatialreference.org/ref/epsg/900913/proj4>`_
+ requires the ``null`` grid file only included in the extra datum shifting files.
+ It is easier to install the shifting files now, then to have debug a problem caused by their absence later.
+.. [#] Specifically, GeoDjango provides support for the `OGR <http://gdal.org/ogr>`_ library, a component of GDAL.
+.. [#] See `GDAL ticket #2382 <http://trac.osgeo.org/gdal/ticket/2382>`_.
+.. [#] GeoDjango uses the `find_library <http://docs.python.org/library/ctypes.html#finding-shared-libraries>`_
+ routine from ``ctypes.util`` to locate shared libraries.
+.. [#] The ``psycopg2`` Windows installers are packaged and maintained by
+ `Jason Erickson <http://www.stickpeople.com/projects/python/win-psycopg/>`_.
+.. [#] The source code for the installer is available in the `nsis_installer <http://geodjango.org/hg/nsis_installer/>`_
+ GeoDjango mercurial repository.
diff --git a/parts/django/docs/ref/contrib/gis/layermapping.txt b/parts/django/docs/ref/contrib/gis/layermapping.txt
new file mode 100644
index 0000000..0b09e17
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/layermapping.txt
@@ -0,0 +1,220 @@
+.. _ref-layermapping:
+
+====================================
+``LayerMapping`` data import utility
+====================================
+
+.. module:: django.contrib.gis.utils.layermapping
+ :synopsis: Spatial data import utility for GeoDjango models.
+
+.. currentmodule:: django.contrib.gis.utils
+
+The :class:`LayerMapping` class provides a way to map the contents of
+vector spatial data files (e.g. shapefiles) intoto GeoDjango models.
+
+This utility grew out of the author's personal needs to eliminate
+the code repetition that went into pulling geometries and fields out of
+a vector layer, converting to another coordinate system (e.g. WGS84), and
+then inserting into a GeoDjango model.
+
+.. note::
+
+ Use of :class:`LayerMapping` requires GDAL.
+
+.. warning ::
+
+ GIS data sources, like shapefiles, may be very large. If you find
+ that :class:`LayerMapping` is using too much memory, set
+ :setting:`DEBUG` to ``False`` in your settings. When :setting:`DEBUG`
+ is set to ``True``, Django :ref:`automatically logs <faq-see-raw-sql-queries>`
+ *every* SQL query -- thus, when SQL statements contain geometries, it is
+ easy to consume more memory than is typical.
+
+Example
+=======
+
+1. You need a GDAL-supported data source, like a shapefile (here we're using
+ a simple polygon shapefile, ``test_poly.shp``, with three features)::
+
+ >>> from django.contrib.gis.gdal import DataSource
+ >>> ds = DataSource('test_poly.shp')
+ >>> layer = ds[0]
+ >>> print layer.fields # Exploring the fields in the layer, we only want the 'str' field.
+ ['float', 'int', 'str']
+ >>> print len(layer) # getting the number of features in the layer (should be 3)
+ 3
+ >>> print layer.geom_type # Should be 'Polygon'
+ Polygon
+ >>> print layer.srs # WGS84 in WKT
+ GEOGCS["GCS_WGS_1984",
+ DATUM["WGS_1984",
+ SPHEROID["WGS_1984",6378137,298.257223563]],
+ PRIMEM["Greenwich",0],
+ UNIT["Degree",0.017453292519943295]]
+
+2. Now we define our corresponding Django model (make sure to use ``syncdb``)::
+
+ from django.contrib.gis.db import models
+
+ class TestGeo(models.Model):
+ name = models.CharField(max_length=25) # corresponds to the 'str' field
+ poly = models.PolygonField(srid=4269) # we want our model in a different SRID
+ objects = models.GeoManager()
+ def __unicode__(self):
+ return 'Name: %s' % self.name
+
+3. Use :class:`LayerMapping` to extract all the features and place them in the
+ database::
+
+ >>> from django.contrib.gis.utils import LayerMapping
+ >>> from geoapp.models import TestGeo
+ >>> mapping = {'name' : 'str', # The 'name' model field maps to the 'str' layer field.
+ 'poly' : 'POLYGON', # For geometry fields use OGC name.
+ } # The mapping is a dictionary
+ >>> lm = LayerMapping(TestGeo, 'test_poly.shp', mapping)
+ >>> lm.save(verbose=True) # Save the layermap, imports the data.
+ Saved: Name: 1
+ Saved: Name: 2
+ Saved: Name: 3
+
+Here, :class:`LayerMapping` just transformed the three geometries from the
+shapefile in their original spatial reference system (WGS84) to the spatial
+reference system of the GeoDjango model (NAD83). If no spatial reference
+system is defined for the layer, use the ``source_srs`` keyword with a
+:class:`~django.contrib.gis.gdal.SpatialReference` object to specify one.
+
+``LayerMapping`` API
+====================
+
+.. class:: LayerMapping(model, data_source, mapping[, layer=0, source_srs=None, encoding=None, transaction_mode='commit_on_success', transform=True, unique=True, using='default'])
+
+The following are the arguments and keywords that may be used during
+instantiation of ``LayerMapping`` objects.
+
+================= =========================================================
+Argument Description
+================= =========================================================
+``model`` The geographic model, *not* an instance.
+
+``data_source`` The path to the OGR-supported data source file
+ (e.g., a shapefile). Also accepts
+ :class:`django.contrib.gis.gdal.DataSource` instances.
+
+``mapping`` A dictionary: keys are strings corresponding to
+ the model field, and values correspond to
+ string field names for the OGR feature, or if the
+ model field is a geographic then it should
+ correspond to the OGR geometry type,
+ e.g., ``'POINT'``, ``'LINESTRING'``, ``'POLYGON'``.
+================= =========================================================
+
+===================== =====================================================
+Keyword Arguments
+===================== =====================================================
+``layer`` The index of the layer to use from the Data Source
+ (defaults to 0)
+
+``source_srs`` Use this to specify the source SRS manually (for
+ example, some shapefiles don't come with a '.prj'
+ file). An integer SRID, WKT or PROJ.4 strings, and
+ :class:`django.contrib.gis.gdal.SpatialReference`
+ objects are accepted.
+
+``encoding`` Specifies the character set encoding of the strings
+ in the OGR data source. For example, ``'latin-1'``,
+ ``'utf-8'``, and ``'cp437'`` are all valid encoding
+ parameters.
+
+``transaction_mode`` May be ``'commit_on_success'`` (default) or
+ ``'autocommit'``.
+
+``transform`` Setting this to False will disable coordinate
+ transformations. In other words, geometries will
+ be inserted into the database unmodified from their
+ original state in the data source.
+
+``unique`` Setting this to the name, or a tuple of names,
+ from the given model will create models unique
+ only to the given name(s). Geometries will from
+ each feature will be added into the collection
+ associated with the unique model. Forces
+ the transaction mode to be ``'autocommit'``.
+
+``using`` New in version 1.2. Sets the database to use when
+ importing spatial data. Default is ``'default'``
+===================== =====================================================
+
+``save()`` Keyword Arguments
+----------------------------
+
+.. method:: LayerMapping.save([verbose=False, fid_range=False, step=False, progress=False, silent=False, stream=sys.stdout, strict=False])
+
+The ``save()`` method also accepts keywords. These keywords are
+used for controlling output logging, error handling, and for importing
+specific feature ranges.
+
+=========================== =================================================
+Save Keyword Arguments Description
+=========================== =================================================
+``fid_range`` May be set with a slice or tuple of
+ (begin, end) feature ID's to map from
+ the data source. In other words, this
+ keyword enables the user to selectively
+ import a subset range of features in the
+ geographic data source.
+
+``progress`` When this keyword is set, status information
+ will be printed giving the number of features
+ processed and successfully saved. By default,
+ progress information will be printed every 1000
+ features processed, however, this default may
+ be overridden by setting this keyword with an
+ integer for the desired interval.
+
+``silent`` By default, non-fatal error notifications are
+ printed to ``sys.stdout``, but this keyword may
+ be set to disable these notifications.
+
+``step`` If set with an integer, transactions will
+ occur at every step interval. For example, if
+ ``step=1000``, a commit would occur after the
+ 1,000th feature, the 2,000th feature etc.
+
+
+``stream`` Status information will be written to this file
+ handle. Defaults to using ``sys.stdout``, but
+ any object with a ``write`` method is supported.
+
+``strict`` Execution of the model mapping will cease upon
+ the first error encountered. The default value
+ (``False``)
+ behavior is to attempt to continue.
+
+``verbose`` If set, information will be printed
+ subsequent to each model save
+ executed on the database.
+=========================== =================================================
+
+Troubleshooting
+===============
+
+Running out of memory
+---------------------
+
+As noted in the warning at the top of this section, Django stores all SQL
+queries when ``DEBUG=True``. Set ``DEBUG=False`` in your settings, and this
+should stop excessive memory use when running ``LayerMapping`` scripts.
+
+MySQL: ``max_allowed_packet`` error
+-----------------------------------
+
+If you encounter the following error when using ``LayerMapping`` and MySQL::
+
+ OperationalError: (1153, "Got a packet bigger than 'max_allowed_packet' bytes")
+
+Then the solution is to increase the value of the ``max_allowed_packet``
+setting in your MySQL configuration. For example, the default value may
+be something low like one megabyte -- the setting may be modified in MySQL's
+configuration file (``my.cnf``) in the ``[mysqld]`` section::
+
+ max_allowed_packet = 10M
diff --git a/parts/django/docs/ref/contrib/gis/measure.txt b/parts/django/docs/ref/contrib/gis/measure.txt
new file mode 100644
index 0000000..6971788
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/measure.txt
@@ -0,0 +1,180 @@
+.. _ref-measure:
+
+===================
+Measurement Objects
+===================
+
+.. module:: django.contrib.gis.measure
+ :synopsis: GeoDjango's distance and area measurment objects.
+
+The :mod:`django.contrib.gis.measure` module contains objects that allow
+for convenient representation of distance and area units of measure. [#]_
+Specifically, it implements two objects, :class:`Distance` and
+:class:`Area` -- both of which may be accessed via the
+:class:`D` and :class:`A` convenience aliases, respectively.
+
+Example
+=======
+
+:class:`Distance` objects may be instantiated using a keyword argument indicating the
+context of the units. In the example below, two different distance objects are
+instantiated in units of kilometers (``km``) and miles (``mi``)::
+
+ >>> from django.contrib.gis.measure import Distance, D
+ >>> d1 = Distance(km=5)
+ >>> print d1
+ 5.0 km
+ >>> d2 = D(mi=5) # `D` is an alias for `Distance`
+ >>> print d2
+ 5.0 mi
+
+Conversions are easy, just access the preferred unit attribute to get a
+converted distance quantity::
+
+ >>> print d1.mi # Converting 5 kilometers to miles
+ 3.10685596119
+ >>> print d2.km # Converting 5 miles to kilometers
+ 8.04672
+
+Moreover, arithmetic operations may be performed between the distance
+objects::
+
+ >>> print d1 + d2 # Adding 5 miles to 5 kilometers
+ 13.04672 km
+ >>> print d2 - d1 # Subtracting 5 kilometers from 5 miles
+ 1.89314403881 mi
+
+Two :class:`Distance` objects multiplied together will yield an :class:`Area`
+object, which uses squared units of measure::
+
+ >>> a = d1 * d2 # Returns an Area object.
+ >>> print a
+ 40.2336 sq_km
+
+To determine what the attribute abbreviation of a unit is, the ``unit_attname``
+class method may be used::
+
+ >>> print Distance.unit_attname('US Survey Foot')
+ survey_ft
+ >>> print Distance.unit_attname('centimeter')
+ cm
+
+.. _supported_units:
+
+Supported units
+===============
+
+================================= ========================================
+Unit Attribute Full name or alias(es)
+================================= ========================================
+``km`` Kilometre, Kilometer
+``mi`` Mile
+``m`` Meter, Metre
+``yd`` Yard
+``ft`` Foot, Foot (International)
+``survey_ft`` U.S. Foot, US survey foot
+``inch`` Inches
+``cm`` Centimeter
+``mm`` Millimetre, Millimeter
+``um`` Micrometer, Micrometre
+``british_ft`` British foot (Sears 1922)
+``british_yd`` British yard (Sears 1922)
+``british_chain_sears`` British chain (Sears 1922)
+``indian_yd`` Indian yard, Yard (Indian)
+``sears_yd`` Yard (Sears)
+``clarke_ft`` Clarke's Foot
+``chain`` Chain
+``chain_benoit`` Chain (Benoit)
+``chain_sears`` Chain (Sears)
+``british_chain_benoit`` British chain (Benoit 1895 B)
+``british_chain_sears_truncated`` British chain (Sears 1922 truncated)
+``gold_coast_ft`` Gold Coast foot
+``link`` Link
+``link_benoit`` Link (Benoit)
+``link_sears`` Link (Sears)
+``clarke_link`` Clarke's link
+``fathom`` Fathom
+``rod`` Rod
+``nm`` Nautical Mile
+``nm_uk`` Nautical Mile (UK)
+``german_m`` German legal metre
+================================= ========================================
+
+.. note::
+
+ :class:`Area` attributes are the same as :class:`Distance` attributes,
+ except they are prefixed with ``sq_`` (area units are square in nature).
+ For example, ``Area(sq_m=2)`` creates an :class:`Area` object
+ representing two square meters.
+
+Measurement API
+===============
+
+``Distance``
+------------
+
+.. class:: Distance(**kwargs)
+
+ To initialize a distance object, pass in a keyword corresponding to
+ the desired :ref:`unit attribute name <supported_units>` set with
+ desired value. For example, the following creates a distance
+ object representing 5 miles::
+
+ >>> dist = Distance(mi=5)
+
+ .. method:: __getattr__(unit_att)
+
+ Returns the distance value in units corresponding to the given unit
+ attribute. For example::
+
+ >>> print dist.km
+ 8.04672
+
+ .. classmethod:: unit_attname(unit_name)
+
+ Returns the distance unit attribute name for the given full unit name.
+ For example::
+
+ >>> Distance.unit_attname('Mile')
+ 'mi'
+
+.. class:: D
+
+ Alias for :class:`Distance` class.
+
+``Area``
+--------
+
+.. class:: Area(**kwargs)
+
+ To initialize a distance object, pass in a keyword corresponding to
+ the desired :ref:`unit attribute name <supported_units>` set with
+ desired value. For example, the following creates a distance
+ object representing 5 square miles::
+
+ >>> a = Area(sq_mi=5)
+
+ .. method:: __getattr__(unit_att)
+
+ Returns the area value in units corresponding to the given unit
+ attribute. For example::
+
+ >>> print a.sq_km
+ 12.949940551680001
+
+ .. classmethod:: unit_attname(unit_name)
+
+ Returns the area unit attribute name for the given full unit name.
+ For example::
+
+ >>> Area.unit_attname('Kilometer')
+ 'sq_km'
+
+.. class:: A
+
+ Alias for :class:`Area` class.
+
+.. rubric:: Footnotes
+.. [#] `Robert Coup <http://koordinates.com/>`_ is the initial author of the measure objects,
+ and was inspired by Brian Beck's work in `geopy <http://code.google.com/p/geopy/>`_
+ and Geoff Biggs' PhD work on dimensioned units for robotics.
diff --git a/parts/django/docs/ref/contrib/gis/model-api.txt b/parts/django/docs/ref/contrib/gis/model-api.txt
new file mode 100644
index 0000000..6b50cf3
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/model-api.txt
@@ -0,0 +1,265 @@
+.. _ref-gis-model-api:
+
+===================
+GeoDjango Model API
+===================
+
+.. module:: django.contrib.gis.db.models
+ :synopsis: GeoDjango model and field API.
+
+This document explores the details of the GeoDjango Model API. Throughout this
+section, we'll be using the following geographic model of a `ZIP code`__ as our
+example::
+
+ from django.contrib.gis.db import models
+
+ class Zipcode(models.Model):
+ code = models.CharField(max_length=5)
+ poly = models.PolygonField()
+ objects = models.GeoManager()
+
+__ http://en.wikipedia.org/wiki/ZIP_code
+
+Geometry Field Types
+====================
+
+Each of the following geometry field types correspond with the
+OpenGIS Simple Features specification [#fnogc]_.
+
+``GeometryField``
+-----------------
+
+.. class:: GeometryField
+
+``PointField``
+--------------
+
+.. class:: PointField
+
+``LineStringField``
+-------------------
+
+.. class:: LineStringField
+
+``PolygonField``
+----------------
+
+.. class:: PolygonField
+
+``MultiPointField``
+-------------------
+
+.. class:: MultiPointField
+
+``MultiLineStringField``
+------------------------
+
+.. class:: MultiLineStringField
+
+``MultiPolygonField``
+---------------------
+
+.. class:: MultiPolygonField
+
+``GeometryCollectionField``
+---------------------------
+
+.. class:: GeometryCollectionField
+
+.. _geometry-field-options:
+
+Geometry Field Options
+======================
+
+In addition to the regular :ref:`common-model-field-options` available for
+Django model fields, geometry fields have the following additional options.
+All are optional.
+
+``srid``
+--------
+
+.. attribute:: GeometryField.srid
+
+Sets the SRID [#fnogcsrid]_ (Spatial Reference System Identity) of the geometry field to
+the given value. Defaults to 4326 (also known as `WGS84`__, units are in degrees
+of longitude and latitude).
+
+__ http://en.wikipedia.org/wiki/WGS84
+
+.. _selecting-an-srid:
+
+Selecting an SRID
+^^^^^^^^^^^^^^^^^
+
+Choosing an appropriate SRID for your model is an important decision that the
+developer should consider carefully. The SRID is an integer specifier that
+corresponds to the projection system that will be used to interpret the data
+in the spatial database. [#fnsrid]_ Projection systems give the context to the
+coordinates that specify a location. Although the details of `geodesy`__ are
+beyond the scope of this documentation, the general problem is that the earth
+is spherical and representations of the earth (e.g., paper maps, Web maps)
+are not.
+
+Most people are familiar with using latitude and longitude to reference a
+location on the earth's surface. However, latitude and longitude are angles,
+not distances. [#fnharvard]_ In other words, while the shortest path between two points on
+a flat surface is a straight line, the shortest path between two points on a curved
+surface (such as the earth) is an *arc* of a `great circle`__. [#fnthematic]_ Thus,
+additional computation is required to obtain distances in planar units (e.g.,
+kilometers and miles). Using a geographic coordinate system may introduce
+complications for the developer later on. For example, PostGIS versions 1.4
+and below do not have the capability to perform distance calculations between
+non-point geometries using geographic coordinate systems, e.g., constructing a
+query to find all points within 5 miles of a county boundary stored as WGS84.
+[#fndist]_
+
+Portions of the earth's surface may projected onto a two-dimensional, or
+Cartesian, plane. Projected coordinate systems are especially convenient
+for region-specific applications, e.g., if you know that your database will
+only cover geometries in `North Kansas`__, then you may consider using projection
+system specific to that region. Moreover, projected coordinate systems are
+defined in Cartesian units (such as meters or feet), easing distance
+calculations.
+
+.. note::
+
+ If you wish to peform arbitrary distance queries using non-point
+ geometries in WGS84, consider upgrading to PostGIS 1.5. For
+ better performance, enable the :attr:`GeometryField.geography`
+ keyword so that :ref:`geography database type <geography-type>`
+ is used instead.
+
+Additional Resources:
+
+* `spatialreference.org`__: A Django-powered database of spatial reference
+ systems.
+* `The State Plane Coordinate System`__: A Web site covering the various
+ projection systems used in the United States. Much of the U.S. spatial
+ data encountered will be in one of these coordinate systems rather than
+ in a geographic coordinate system such as WGS84.
+
+__ http://en.wikipedia.org/wiki/Geodesy
+__ http://en.wikipedia.org/wiki/Great_circle
+__ http://www.spatialreference.org/ref/epsg/2796/
+__ http://spatialreference.org/
+__ http://welcome.warnercnr.colostate.edu/class_info/nr502/lg3/datums_coordinates/spcs.html
+
+``spatial_index``
+-----------------
+
+.. attribute:: GeometryField.spatial_index
+
+Defaults to ``True``. Creates a spatial index for the given geometry
+field.
+
+.. note::
+
+ This is different from the ``db_index`` field option because spatial
+ indexes are created in a different manner than regular database
+ indexes. Specifically, spatial indexes are typically created using
+ a variant of the R-Tree, while regular database indexes typically
+ use B-Trees.
+
+``dim``
+-------
+
+.. versionadded:: 1.2
+
+.. attribute:: GeometryField.dim
+
+This option may be used for customizing the coordinate dimension of the
+geometry field. By default, it is set to 2, for representing two-dimensional
+geometries. For spatial backends that support it, it may be set to 3 for
+three-dimensonal support.
+
+.. note::
+
+ At this time 3D support requires that GEOS 3.1 be installed, and is
+ limited only to the PostGIS spatial backend.
+
+``geography``
+-------------
+
+.. versionadded:: 1.2
+
+.. attribute:: GeometryField.geography
+
+If set to ``True``, this option will create a database column of
+type geography, rather than geometry. Please refer to the
+:ref:`geography type <geography-type>` section below for more
+details.
+
+.. note::
+
+ Geography support is limited only to PostGIS 1.5+, and will
+ force the SRID to be 4326.
+
+.. _geography-type:
+
+Geography Type
+^^^^^^^^^^^^^^
+
+In PostGIS 1.5, the geography type was introduced -- it provides
+provides native support for spatial features represented with geographic
+coordinates (e.g., WGS84 longitude/latitude). [#fngeography]_
+Unlike the plane used by a geometry type, the geography type uses a spherical
+representation of its data. Distance and measurement operations
+performed on a geography column automatically employ great circle arc
+calculations and return linear units. In other words, when ``ST_Distance``
+is called on two geographies, a value in meters is returned (as opposed
+to degrees if called on a geometry column in WGS84).
+
+Because geography calculations involve more mathematics, only a subset of the
+PostGIS spatial lookups are available for the geography type. Practically,
+this means that in addition to the :ref:`distance lookups <distance-lookups>`
+only the following additional :ref:`spatial lookups <spatial-lookups>` are
+available for geography columns:
+
+* :lookup:`bboverlaps`
+* :lookup:`coveredby`
+* :lookup:`covers`
+* :lookup:`intersects`
+
+For more information, the PostGIS documentation contains a helpful section on
+determining `when to use geography data type over geometry data type
+<http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_GeographyVSGeometry>`_.
+
+``GeoManager``
+==============
+
+.. currentmodule:: django.contrib.gis.db.models
+.. class:: GeoManager
+
+In order to conduct geographic queries, each geographic model requires
+a ``GeoManager`` model manager. This manager allows for the proper SQL
+construction for geographic queries; thus, without it, all geographic filters
+will fail. It should also be noted that ``GeoManager`` is required even if the
+model does not have a geographic field itself, e.g., in the case of a
+``ForeignKey`` relation to a model with a geographic field. For example,
+if we had an ``Address`` model with a ``ForeignKey`` to our ``Zipcode``
+model::
+
+ from django.contrib.gis.db import models
+ from django.contrib.localflavor.us.models import USStateField
+
+ class Address(models.Model):
+ num = models.IntegerField()
+ street = models.CharField(max_length=100)
+ city = models.CharField(max_length=100)
+ state = USStateField()
+ zipcode = models.ForeignKey(Zipcode)
+ objects = models.GeoManager()
+
+The geographic manager is needed to do spatial queries on related ``Zipcode`` objects,
+for example::
+
+ qs = Address.objects.filter(zipcode__poly__contains='POINT(-104.590948 38.319914)')
+
+.. rubric:: Footnotes
+.. [#fnogc] OpenGIS Consortium, Inc., `Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999).
+.. [#fnogcsrid] *See id.* at Ch. 2.3.8, p. 39 (Geometry Values and Spatial Reference Systems).
+.. [#fnsrid] Typically, SRID integer corresponds to an EPSG (`European Petroleum Survey Group <http://www.epsg.org>`_) identifier. However, it may also be associated with custom projections defined in spatial database's spatial reference systems table.
+.. [#fnharvard] Harvard Graduate School of Design, `An Overview of Geodesy and Geographic Referencing Systems <http://www.gsd.harvard.edu/gis/manual/projections/fundamentals/>`_. This is an excellent resource for an overview of principles relating to geographic and Cartesian coordinate systems.
+.. [#fnthematic] Terry A. Slocum, Robert B. McMaster, Fritz C. Kessler, & Hugh H. Howard, *Thematic Cartography and Geographic Visualization* (Prentice Hall, 2nd edition), at Ch. 7.1.3.
+.. [#fndist] This limitation does not apply to PostGIS 1.5. It should be noted that even in previous versions of PostGIS, this isn't impossible using GeoDjango; you could for example, take a known point in a projected coordinate system, buffer it to the appropriate radius, and then perform an intersection operation with the buffer transformed to the geographic coordinate system.
+.. [#fngeography] Please refer to the `PostGIS Geography Type <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_Geography>`_ documentation for more details.
diff --git a/parts/django/docs/ref/contrib/gis/ogrinspect.txt b/parts/django/docs/ref/contrib/gis/ogrinspect.txt
new file mode 100644
index 0000000..ed285e0
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/ogrinspect.txt
@@ -0,0 +1,21 @@
+.. _ref-ogrinspect:
+
+==============
+OGR Inspection
+==============
+
+.. module:: django.contrib.gis.utils.ogrinspect
+ :synopsis: Utilities for inspecting OGR data sources.
+
+.. currentmodule:: django.contrib.gis.utils
+
+``ogrinspect``
+==============
+
+.. function:: ogrinspect(data_source, model_name[, **kwargs])
+ :noindex:
+
+``mapping``
+===========
+
+.. function:: mapping(data_source, [geom_name='geom', layer_key=0, multi_geom=False])
diff --git a/parts/django/docs/ref/contrib/gis/sitemaps.txt b/parts/django/docs/ref/contrib/gis/sitemaps.txt
new file mode 100644
index 0000000..75bddd3
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/sitemaps.txt
@@ -0,0 +1,27 @@
+===================
+Geographic Sitemaps
+===================
+
+Google's sitemap protocol has been recently extended to support geospatial
+content. [#]_ This includes the addition of the ``<url>`` child element
+``<geo:geo>``, which tells Google that the content located at the URL is
+geographic in nature. [#]_
+
+Example
+=======
+
+Reference
+=========
+
+``KMLSitemap``
+--------------
+
+``KMZSitemap``
+--------------
+
+``GeoRSSSitemap``
+-----------------
+
+.. rubric:: Footnotes
+.. [#] Google, Inc., `What is a Geo Sitemap? <http://www.google.com/support/webmasters/bin/answer.py?answer=94554>`_.
+.. [#] Google, Inc., `Submit Your Geo Content to Google <http://code.google.com/apis/kml/documentation/kmlSearch.html>`_.
diff --git a/parts/django/docs/ref/contrib/gis/testing.txt b/parts/django/docs/ref/contrib/gis/testing.txt
new file mode 100644
index 0000000..889b43a
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/testing.txt
@@ -0,0 +1,268 @@
+======================
+Testing GeoDjango Apps
+======================
+
+.. versionchanged:: 1.2
+
+In Django 1.2, the addition of :ref:`spatial-backends`
+simplified the process of testing GeoDjango applications. Specifically, testing
+GeoDjango applications is now the same as :doc:`/topics/testing`.
+
+Included in this documentation are some additional notes and settings
+for :ref:`testing-postgis` and :ref:`testing-spatialite` users.
+
+.. note::
+
+ Django 1.1 users are still required to use a custom :setting:`TEST_RUNNER`.
+ See the :ref:`testing-1.1` section for more details.
+
+.. _testing-postgis:
+
+PostGIS
+=======
+
+Settings
+--------
+
+.. note::
+
+ The settings below have sensible defaults, and shouldn't require manual setting.
+
+.. setting:: POSTGIS_TEMPLATE
+
+``POSTGIS_TEMPLATE``
+^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 1.1
+
+.. versionchanged:: 1.2
+
+This setting may be used to customize the name of the PostGIS template
+database to use. In Django versions 1.2 and above, it automatically
+defaults to ``'template_postgis'`` (the same name used in the
+:ref:`installation documentation <spatialdb_template>`).
+
+.. note::
+
+ Django 1.1 users will still have to define the :setting:`POSTGIS_TEMPLATE`
+ with a value, for example::
+
+ POSTGIS_TEMPLATE='template_postgis'
+
+.. setting:: POSTGIS_VERSION
+
+``POSTGIS_VERSION``
+^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 1.1
+
+When GeoDjango's spatial backend initializes on PostGIS, it has to perform
+a SQL query to determine the version in order to figure out what
+features are available. Advanced users wishing to prevent this additional
+query may set the version manually using a 3-tuple of integers specifying
+the major, minor, and subminor version numbers for PostGIS. For example,
+to configure for PostGIS 1.5.2 you would use::
+
+ POSTGIS_VERSION = (1, 5, 2)
+
+Obtaining Sufficient Privileges
+-------------------------------
+
+Depending on your configuration, this section describes several methods to
+configure a database user with sufficient privileges to run tests for
+GeoDjango applications on PostgreSQL. If your
+:ref:`spatial database template <spatialdb_template>`
+was created like in the instructions, then your testing database user
+only needs to have the ability to create databases. In other configurations,
+you may be required to use a database superuser.
+
+Create Database User
+^^^^^^^^^^^^^^^^^^^^
+
+To make database user with the ability to create databases, use the
+following command::
+
+ $ createuser --createdb -R -S <user_name>
+
+The ``-R -S`` flags indicate that we do not want the user to have the ability
+to create additional users (roles) or to be a superuser, respectively.
+
+Alternatively, you may alter an existing user's role from the SQL shell
+(assuming this is done from an existing superuser account)::
+
+ postgres# ALTER ROLE <user_name> CREATEDB NOSUPERUSER NOCREATEROLE;
+
+Create Database Superuser
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This may be done at the time the user is created, for example::
+
+ $ createuser --superuser <user_name>
+
+Or you may alter the user's role from the SQL shell (assuming this
+is done from an existing superuser account)::
+
+ postgres# ALTER ROLE <user_name> SUPERUSER;
+
+
+Create Local PostgreSQL Database
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+1. Initialize database: ``initdb -D /path/to/user/db``
+
+2. If there's already a Postgres instance on the machine, it will need
+ to use a different TCP port than 5432. Edit ``postgresql.conf`` (in
+ ``/path/to/user/db``) to change the database port (e.g. ``port = 5433``).
+
+3. Start this database ``pg_ctl -D /path/to/user/db start``
+
+Windows
+-------
+
+On Windows platforms the pgAdmin III utility may also be used as
+a simple way to add superuser privileges to your database user.
+
+By default, the PostGIS installer on Windows includes a template
+spatial database entitled ``template_postgis``.
+
+.. _testing-spatialite:
+
+SpatiaLite
+==========
+
+.. versionadded:: 1.1
+
+You will need to download the `initialization SQL`__ script for SpatiaLite::
+
+ $ wget http://www.gaia-gis.it/spatialite/init_spatialite-2.3.zip
+ $ unzip init_spatialite-2.3.zip
+
+If ``init_spatialite-2.3.sql`` is in the same path as your project's ``manage.py``,
+then all you have to do is::
+
+ $ python manage.py test
+
+Settings
+--------
+
+.. setting:: SPATIALITE_SQL
+
+``SPATIALITE_SQL``
+^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 1.1
+
+By default, the GeoDjango test runner looks for the SpatiaLite SQL in the
+same directory where it was invoked (by default the same directory where
+``manage.py`` is located). If you want to use a different location, then
+you may add the following to your settings::
+
+ SPATIALITE_SQL='/path/to/init_spatialite-2.3.sql'
+
+__ http://www.gaia-gis.it/spatialite/init_spatialite-2.3.zip
+
+.. _testing-1.1:
+
+Testing GeoDjango Applications in 1.1
+=====================================
+
+In Django 1.1, to accommodate the extra steps required to scaffalod a
+spatial database automatically, a test runner customized for GeoDjango
+must be used. To use this runner, configure :setting:`TEST_RUNNER` as follows::
+
+ TEST_RUNNER='django.contrib.gis.tests.run_tests'
+
+.. note::
+
+ In order to create a spatial database, the :setting:`USER` setting
+ (or :setting:`TEST_USER`, if optionally defined on Oracle) requires
+ elevated privileges. When using PostGIS or MySQL, the database user
+ must have at least the ability to create databases. When testing on Oracle,
+ the user should be a superuser.
+
+.. _geodjango-tests:
+
+GeoDjango Tests
+===============
+
+.. versionchanged:: 1.2.4
+
+GeoDjango's test suite may be run in one of two ways, either by itself or
+with the rest of Django's :ref:`unit-tests`.
+
+.. note::
+
+ The :setting:`TEST_RUNNER` previously used to execute the GeoDjango
+ test suite,:func:`django.contrib.gis.tests.run_gis_tests`, was deprecated
+ in favor of the :class:`django.contrib.gis.tests.GeoDjangoTestSuiteRunner`
+ class.
+
+Run only GeoDjango tests
+------------------------
+
+To run *only* the tests for GeoDjango, the :setting:`TEST_RUNNER`
+setting must be changed to use the
+:class:`~django.contrib.gis.tests.GeoDjangoTestSuiteRunner`::
+
+ TEST_RUNNER = 'django.contrib.gis.tests.GeoDjangoTestSuiteRunner'
+
+Example
+^^^^^^^
+
+First, you'll need a bare-bones settings file, like below, that is
+customized with your spatial database name and user::
+
+ TEST_RUNNER = 'django.contrib.gis.tests.GeoDjangoTestSuiteRunner'
+
+ DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'NAME': 'a_spatial_database',
+ 'USER': 'db_user'
+ }
+ }
+
+Assuming the above is in a file called ``postgis.py`` that is in the
+the same directory as ``manage.py`` of your Django project, then
+you may run the tests with the following command::
+
+ $ python manage.py test --settings=postgis
+
+Run with ``runtests.py``
+------------------------
+
+To have the GeoDjango tests executed when
+:ref:`running the Django test suite <running-unit-tests>` with ``runtests.py``
+all of the databases in the settings file must be using one of the
+:ref:`spatial database backends <spatial-backends>`.
+
+.. warning::
+
+ Do not change the :setting:`TEST_RUNNER` setting
+ when running the GeoDjango tests with ``runtests.py``.
+
+Example
+^^^^^^^
+
+The following is an example bare-bones settings file with spatial backends
+that can be used to run the entire Django test suite, including those
+in :mod:`django.contrib.gis`::
+
+ DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'NAME': 'geodjango',
+ 'USER': 'geodjango',
+ },
+ 'other': {
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'NAME': 'other',
+ 'USER': 'geodjango',
+ }
+ }
+
+Assuming the settings above were in a ``postgis.py`` file in the same
+directory as ``runtests.py``, then all Django and GeoDjango tests would
+be performed when executing the command::
+
+ $ ./runtests.py --settings=postgis
diff --git a/parts/django/docs/ref/contrib/gis/tutorial.txt b/parts/django/docs/ref/contrib/gis/tutorial.txt
new file mode 100644
index 0000000..9deeb78
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/tutorial.txt
@@ -0,0 +1,758 @@
+==================
+GeoDjango Tutorial
+==================
+
+Introduction
+============
+
+GeoDjango is an add-on for Django that turns it into a world-class geographic
+Web framework. GeoDjango strives to make at as simple as possible to create
+geographic Web applications, like location-based services. Some features include:
+
+* Django model fields for `OGC`_ geometries.
+* Extensions to Django's ORM for the querying and manipulation of spatial data.
+* Loosely-coupled, high-level Python interfaces for GIS geometry operations and
+ data formats.
+* Editing of geometry fields inside the admin.
+
+This tutorial assumes a familiarity with Django; thus, if you're brand new to
+Django please read through the :doc:`regular tutorial </intro/tutorial01>` to introduce
+yourself with basic Django concepts.
+
+.. note::
+
+ GeoDjango has special prerequisites overwhat is required by Django --
+ please consult the :ref:`installation documentation <ref-gis-install>`
+ for more details.
+
+This tutorial will guide you through the creation of a geographic Web
+application for viewing the `world borders`_. [#]_ Some of the code
+used in this tutorial is taken from and/or inspired by the `GeoDjango
+basic apps`_ project. [#]_
+
+.. note::
+
+ Proceed through the tutorial sections sequentially for step-by-step
+ instructions.
+
+.. _OGC: http://www.opengeospatial.org/
+.. _world borders: http://thematicmapping.org/downloads/world_borders.php
+.. _GeoDjango basic apps: http://code.google.com/p/geodjango-basic-apps/
+
+Setting Up
+==========
+
+Create a Spatial Database
+-------------------------
+
+.. note::
+
+ MySQL and Oracle users can skip this section because spatial types
+ are already built into the database.
+
+First, a spatial database needs to be created for our project. If using
+PostgreSQL and PostGIS, then the following commands will
+create the database from a :ref:`spatial database template <spatialdb_template>`::
+
+ $ createdb -T template_postgis geodjango
+
+.. note::
+
+ This command must be issued by a database user that has permissions to
+ create a database. Here is an example set of commands to create such
+ a user::
+
+ $ sudo su - postgres
+ $ createuser --createdb geo
+ $ exit
+
+ Replace ``geo`` to correspond to the system login user name will be
+ connecting to the database. For example, ``johndoe`` if that is the
+ system user that will be running GeoDjango.
+
+Users of SQLite and SpatiaLite should consult the instructions on how
+to create a :ref:`SpatiaLite database <create_spatialite_db>`.
+
+Create GeoDjango Project
+------------------------
+
+Use the ``django-admin.py`` script like normal to create a ``geodjango`` project::
+
+ $ django-admin.py startproject geodjango
+
+With the project initialized, now create a ``world`` Django application within
+the ``geodjango`` project::
+
+ $ cd geodjango
+ $ python manage.py startapp world
+
+Configure ``settings.py``
+-------------------------
+
+The ``geodjango`` project settings are stored in the ``settings.py`` file. Edit
+the database connection settings appropriately::
+
+ DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'NAME': 'geodjango',
+ 'USER': 'geo',
+ }
+ }
+
+.. note::
+
+ These database settings are for Django 1.2 and above.
+
+In addition, modify the :setting:`INSTALLED_APPS` setting to include
+:mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
+and ``world`` (our newly created application)::
+
+ INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.admin',
+ 'django.contrib.gis',
+ 'world'
+ )
+
+Geographic Data
+===============
+
+.. _worldborders:
+
+World Borders
+-------------
+
+The world borders data is available in this `zip file`__. Create a data directory
+in the ``world`` application, download the world borders data, and unzip.
+On GNU/Linux platforms the following commands should do it::
+
+ $ mkdir world/data
+ $ cd world/data
+ $ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
+ $ unzip TM_WORLD_BORDERS-0.3.zip
+ $ cd ../..
+
+The world borders ZIP file contains a set of data files collectively known as
+an `ESRI Shapefile`__, one of the most popular geospatial data formats. When
+unzipped the world borders data set includes files with the following extensions:
+
+* ``.shp``: Holds the vector data for the world borders geometries.
+* ``.shx``: Spatial index file for geometries stored in the ``.shp``.
+* ``.dbf``: Database file for holding non-geometric attribute data
+ (e.g., integer and character fields).
+* ``.prj``: Contains the spatial reference information for the geographic
+ data stored in the shapefile.
+
+__ http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
+__ http://en.wikipedia.org/wiki/Shapefile
+
+Use ``ogrinfo`` to examine spatial data
+---------------------------------------
+
+The GDAL ``ogrinfo`` utility is excellent for examining metadata about
+shapefiles (or other vector data sources)::
+
+ $ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
+ INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
+ using driver `ESRI Shapefile' successful.
+ 1: TM_WORLD_BORDERS-0.3 (Polygon)
+
+Here ``ogrinfo`` is telling us that the shapefile has one layer, and that
+layer contains polygon data. To find out more we'll specify the layer name
+and use the ``-so`` option to get only important summary information::
+
+ $ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3
+ INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
+ using driver `ESRI Shapefile' successful.
+
+ Layer name: TM_WORLD_BORDERS-0.3
+ Geometry: Polygon
+ Feature Count: 246
+ Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
+ Layer SRS WKT:
+ GEOGCS["GCS_WGS_1984",
+ DATUM["WGS_1984",
+ SPHEROID["WGS_1984",6378137.0,298.257223563]],
+ PRIMEM["Greenwich",0.0],
+ UNIT["Degree",0.0174532925199433]]
+ FIPS: String (2.0)
+ ISO2: String (2.0)
+ ISO3: String (3.0)
+ UN: Integer (3.0)
+ NAME: String (50.0)
+ AREA: Integer (7.0)
+ POP2005: Integer (10.0)
+ REGION: Integer (3.0)
+ SUBREGION: Integer (3.0)
+ LON: Real (8.3)
+ LAT: Real (7.3)
+
+This detailed summary information tells us the number of features in the layer
+(246), the geographical extent, the spatial reference system ("SRS WKT"),
+as well as detailed information for each attribute field. For example,
+``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field
+with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point
+field that holds a maximum of 8 digits up to three decimal places. Although
+this information may be found right on the `world borders`_ Web site, this shows
+you how to determine this information yourself when such metadata is not
+provided.
+
+Geographic Models
+=================
+
+Defining a Geographic Model
+---------------------------
+
+Now that we've examined our world borders data set using ``ogrinfo``, we can
+create a GeoDjango model to represent this data::
+
+ from django.contrib.gis.db import models
+
+ class WorldBorders(models.Model):
+ # Regular Django fields corresponding to the attributes in the
+ # world borders shapefile.
+ name = models.CharField(max_length=50)
+ area = models.IntegerField()
+ pop2005 = models.IntegerField('Population 2005')
+ fips = models.CharField('FIPS Code', max_length=2)
+ iso2 = models.CharField('2 Digit ISO', max_length=2)
+ iso3 = models.CharField('3 Digit ISO', max_length=3)
+ un = models.IntegerField('United Nations Code')
+ region = models.IntegerField('Region Code')
+ subregion = models.IntegerField('Sub-Region Code')
+ lon = models.FloatField()
+ lat = models.FloatField()
+
+ # GeoDjango-specific: a geometry field (MultiPolygonField), and
+ # overriding the default manager with a GeoManager instance.
+ mpoly = models.MultiPolygonField()
+ objects = models.GeoManager()
+
+ # So the model is pluralized correctly in the admin.
+ class Meta:
+ verbose_name_plural = "World Borders"
+
+ # Returns the string representation of the model.
+ def __unicode__(self):
+ return self.name
+
+Two important things to note:
+
+1. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
+2. The model overrides its default manager with
+ :class:`~django.contrib.gis.db.models.GeoManager`; this is *required*
+ to perform spatial queries.
+
+When declaring a geometry field on your model the default spatial reference system
+is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in
+longitude/latitude pairs in units of degrees. If you want the coordinate system to be
+different, then SRID of the geometry field may be customized by setting the ``srid``
+with an integer corresponding to the coordinate system of your choice.
+
+__ http://en.wikipedia.org/wiki/SRID
+
+Run ``syncdb``
+--------------
+
+After you've defined your model, it needs to be synced with the spatial database.
+First, let's look at the SQL that will generate the table for the ``WorldBorders``
+model::
+
+ $ python manage.py sqlall world
+
+This management command should produce the following output::
+
+ BEGIN;
+ CREATE TABLE "world_worldborders" (
+ "id" serial NOT NULL PRIMARY KEY,
+ "name" varchar(50) NOT NULL,
+ "area" integer NOT NULL,
+ "pop2005" integer NOT NULL,
+ "fips" varchar(2) NOT NULL,
+ "iso2" varchar(2) NOT NULL,
+ "iso3" varchar(3) NOT NULL,
+ "un" integer NOT NULL,
+ "region" integer NOT NULL,
+ "subregion" integer NOT NULL,
+ "lon" double precision NOT NULL,
+ "lat" double precision NOT NULL
+ )
+ ;
+ SELECT AddGeometryColumn('world_worldborders', 'mpoly', 4326, 'MULTIPOLYGON', 2);
+ ALTER TABLE "world_worldborders" ALTER "mpoly" SET NOT NULL;
+ CREATE INDEX "world_worldborders_mpoly_id" ON "world_worldborders" USING GIST ( "mpoly" GIST_GEOMETRY_OPS );
+ COMMIT;
+
+If satisfied, you may then create this table in the database by running the
+``syncdb`` management command::
+
+ $ python manage.py syncdb
+ Creating table world_worldborders
+ Installing custom SQL for world.WorldBorders model
+
+The ``syncdb`` command may also prompt you to create an admin user; go ahead and
+do so (not required now, may be done at any point in the future using the
+``createsuperuser`` management command).
+
+Importing Spatial Data
+======================
+
+This section will show you how to take the data from the world borders
+shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`.
+There are many different different ways to import data in to a
+spatial database -- besides the tools included within GeoDjango, you
+may also use the following to populate your spatial database:
+
+* `ogr2ogr`_: Command-line utility, included with GDAL, that
+ supports loading a multitude of vector data formats into
+ the PostGIS, MySQL, and Oracle spatial databases.
+* `shp2pgsql`_: This utility is included with PostGIS and only supports
+ ESRI shapefiles.
+
+.. _ogr2ogr: http://www.gdal.org/ogr2ogr.html
+.. _shp2pgsql: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#shp2pgsql_usage
+
+.. _gdalinterface:
+
+GDAL Interface
+--------------
+
+Earlier we used the the ``ogrinfo`` to explore the contents of the world borders
+shapefile. Included within GeoDjango is an interface to GDAL's powerful OGR
+library -- in other words, you'll be able explore all the vector data sources
+that OGR supports via a Pythonic API.
+
+First, invoke the Django shell::
+
+ $ python manage.py shell
+
+If the :ref:`worldborders` data was downloaded like earlier in the
+tutorial, then we can determine the path using Python's built-in
+``os`` module::
+
+ >>> import os
+ >>> from geodjango import world
+ >>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
+ ... 'data/TM_WORLD_BORDERS-0.3.shp'))
+
+Now, the world borders shapefile may be opened using GeoDjango's
+:class:`~django.contrib.gis.gdal.DataSource` interface::
+
+ >>> from django.contrib.gis.gdal import *
+ >>> ds = DataSource(world_shp)
+ >>> print ds
+ / ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
+
+Data source objects can have different layers of geospatial features; however,
+shapefiles are only allowed to have one layer::
+
+ >>> print len(ds)
+ 1
+ >>> lyr = ds[0]
+ >>> print lyr
+ TM_WORLD_BORDERS-0.3
+
+You can see what the geometry type of the layer is and how many features it
+contains::
+
+ >>> print lyr.geom_type
+ Polygon
+ >>> print len(lyr)
+ 246
+
+.. note::
+
+ Unfortunately the shapefile data format does not allow for greater
+ specificity with regards to geometry types. This shapefile, like
+ many others, actually includes ``MultiPolygon`` geometries in its
+ features. You need to watch out for this when creating your models
+ as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon``
+ type geometry -- thus a ``MultiPolygonField`` is used in our model's
+ definition instead.
+
+The :class:`~django.contrib.gis.gdal.Layer` may also have a spatial reference
+system associated with it -- if it does, the ``srs`` attribute will return a
+:class:`~django.contrib.gis.gdal.SpatialReference` object::
+
+ >>> srs = lyr.srs
+ >>> print srs
+ GEOGCS["GCS_WGS_1984",
+ DATUM["WGS_1984",
+ SPHEROID["WGS_1984",6378137.0,298.257223563]],
+ PRIMEM["Greenwich",0.0],
+ UNIT["Degree",0.0174532925199433]]
+ >>> srs.proj4 # PROJ.4 representation
+ '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '
+
+Here we've noticed that the shapefile is in the popular WGS84 spatial reference
+system -- in other words, the data uses units of degrees longitude and latitude.
+
+In addition, shapefiles also support attribute fields that may contain
+additional data. Here are the fields on the World Borders layer:
+
+ >>> print lyr.fields
+ ['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']
+
+Here we are examining the OGR types (e.g., whether a field is an integer or
+a string) associated with each of the fields:
+
+ >>> [fld.__name__ for fld in lyr.field_types]
+ ['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']
+
+You can iterate over each feature in the layer and extract information from both
+the feature's geometry (accessed via the ``geom`` attribute) as well as the
+feature's attribute fields (whose **values** are accessed via ``get()``
+method)::
+
+ >>> for feat in lyr:
+ ... print feat.get('NAME'), feat.geom.num_points
+ ...
+ Guernsey 18
+ Jersey 26
+ South Georgia South Sandwich Islands 338
+ Taiwan 363
+
+:class:`~django.contrib.gis.gdal.Layer` objects may be sliced::
+
+ >>> lyr[0:2]
+ [<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>]
+
+And individual features may be retrieved by their feature ID::
+
+ >>> feat = lyr[234]
+ >>> print feat.get('NAME')
+ San Marino
+
+Here the boundary geometry for San Marino is extracted and looking
+exported to WKT and GeoJSON::
+
+ >>> geom = feat.geom
+ >>> print geom.wkt
+ POLYGON ((12.415798 43.957954,12.450554 ...
+ >>> print geom.json
+ { "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
+
+
+``LayerMapping``
+----------------
+
+We're going to dive right in -- create a file called ``load.py`` inside the
+``world`` application, and insert the following::
+
+ import os
+ from django.contrib.gis.utils import LayerMapping
+ from models import WorldBorders
+
+ world_mapping = {
+ 'fips' : 'FIPS',
+ 'iso2' : 'ISO2',
+ 'iso3' : 'ISO3',
+ 'un' : 'UN',
+ 'name' : 'NAME',
+ 'area' : 'AREA',
+ 'pop2005' : 'POP2005',
+ 'region' : 'REGION',
+ 'subregion' : 'SUBREGION',
+ 'lon' : 'LON',
+ 'lat' : 'LAT',
+ 'mpoly' : 'MULTIPOLYGON',
+ }
+
+ world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))
+
+ def run(verbose=True):
+ lm = LayerMapping(WorldBorders, world_shp, world_mapping,
+ transform=False, encoding='iso-8859-1')
+
+ lm.save(strict=True, verbose=verbose)
+
+A few notes about what's going on:
+
+* Each key in the ``world_mapping`` dictionary corresponds to a field in the
+ ``WorldBorders`` model, and the value is the name of the shapefile field
+ that data will be loaded from.
+* The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the
+ geometry type we wish to import as. Even if simple polygons are encountered
+ in the shapefile they will automatically be converted into collections prior
+ to insertion into the database.
+* The path to the shapefile is not absolute -- in other words, if you move the
+ ``world`` application (with ``data`` subdirectory) to a different location,
+ then the script will still work.
+* The ``transform`` keyword is set to ``False`` because the data in the
+ shapefile does not need to be converted -- it's already in WGS84 (SRID=4326).
+* The ``encoding`` keyword is set to the character encoding of string values in
+ the shapefile. This ensures that string values are read and saved correctly
+ from their original encoding system.
+
+Afterwards, invoke the Django shell from the ``geodjango`` project directory::
+
+ $ python manage.py shell
+
+Next, import the ``load`` module, call the ``run`` routine, and watch ``LayerMapping``
+do the work::
+
+ >>> from world import load
+ >>> load.run()
+
+
+.. _ogrinspect-intro:
+
+Try ``ogrinspect``
+------------------
+Now that you've seen how to define geographic models and import data with the
+:ref:`ref-layermapping`, it's possible to further automate this process with
+use of the :djadmin:`ogrinspect` management command. The :djadmin:`ogrinspect`
+command introspects a GDAL-supported vector data source (e.g., a shapefile) and
+generates a model definition and ``LayerMapping`` dictionary automatically.
+
+The general usage of the command goes as follows::
+
+ $ python manage.py ogrinspect [options] <data_source> <model_name> [options]
+
+Where ``data_source`` is the path to the GDAL-supported data source and
+``model_name`` is the name to use for the model. Command-line options may
+be used to further define how the model is generated.
+
+For example, the following command nearly reproduces the ``WorldBorders`` model
+and mapping dictionary created above, automatically::
+
+ $ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorders --srid=4326 --mapping --multi
+
+A few notes about the command-line options given above:
+
+* The ``--srid=4326`` option sets the SRID for the geographic field.
+* The ``--mapping`` option tells ``ogrinspect`` to also generate a
+ mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`.
+* The ``--multi`` option is specified so that the geographic field is a
+ :class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a
+ :class:`~django.contrib.gis.db.models.PolygonField`.
+
+The command produces the following output, which may be copied
+directly into the ``models.py`` of a GeoDjango application::
+
+ # This is an auto-generated Django model module created by ogrinspect.
+ from django.contrib.gis.db import models
+
+ class WorldBorders(models.Model):
+ fips = models.CharField(max_length=2)
+ iso2 = models.CharField(max_length=2)
+ iso3 = models.CharField(max_length=3)
+ un = models.IntegerField()
+ name = models.CharField(max_length=50)
+ area = models.IntegerField()
+ pop2005 = models.IntegerField()
+ region = models.IntegerField()
+ subregion = models.IntegerField()
+ lon = models.FloatField()
+ lat = models.FloatField()
+ geom = models.MultiPolygonField(srid=4326)
+ objects = models.GeoManager()
+
+ # Auto-generated `LayerMapping` dictionary for WorldBorders model
+ worldborders_mapping = {
+ 'fips' : 'FIPS',
+ 'iso2' : 'ISO2',
+ 'iso3' : 'ISO3',
+ 'un' : 'UN',
+ 'name' : 'NAME',
+ 'area' : 'AREA',
+ 'pop2005' : 'POP2005',
+ 'region' : 'REGION',
+ 'subregion' : 'SUBREGION',
+ 'lon' : 'LON',
+ 'lat' : 'LAT',
+ 'geom' : 'MULTIPOLYGON',
+ }
+
+Spatial Queries
+===============
+
+Spatial Lookups
+---------------
+GeoDjango extends the Django ORM and allows the use of spatial lookups.
+Let's do an example where we find the ``WorldBorder`` model that contains
+a point. First, fire up the management shell::
+
+ $ python manage.py shell
+
+Now, define a point of interest [#]_::
+
+ >>> pnt_wkt = 'POINT(-95.3385 29.7245)'
+
+The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude,
+and 29.7245 degrees latitude. The geometry is in a format known as
+Well Known Text (WKT), an open standard issued by the Open Geospatial
+Consortium (OGC). [#]_ Import the ``WorldBorders`` model, and perform
+a ``contains`` lookup using the ``pnt_wkt`` as the parameter::
+
+ >>> from world.models import WorldBorders
+ >>> qs = WorldBorders.objects.filter(mpoly__contains=pnt_wkt)
+ >>> qs
+ [<WorldBorders: United States>]
+
+Here we retrieved a ``GeoQuerySet`` that has only one model: the one
+for the United States (which is what we would expect). Similarly,
+a :ref:`GEOS geometry object <ref-geos>` may also be used -- here the ``intersects``
+spatial lookup is combined with the ``get`` method to retrieve
+only the ``WorldBorders`` instance for San Marino instead of a queryset::
+
+ >>> from django.contrib.gis.geos import Point
+ >>> pnt = Point(12.4604, 43.9420)
+ >>> sm = WorldBorders.objects.get(mpoly__intersects=pnt)
+ >>> sm
+ <WorldBorders: San Marino>
+
+The ``contains`` and ``intersects`` lookups are just a subset of what's
+available -- the :ref:`ref-gis-db-api` documentation has more.
+
+Automatic Spatial Transformations
+---------------------------------
+When querying the spatial database GeoDjango automatically transforms
+geometries if they're in a different coordinate system. In the following
+example, the coordinate will be expressed in terms of `EPSG SRID 32140`__,
+a coordinate system specific to south Texas **only** and in units of
+**meters** and not degrees::
+
+ >>> from django.contrib.gis.geos import *
+ >>> pnt = Point(954158.1, 4215137.1, srid=32140)
+
+Note that ``pnt`` may also constructed with EWKT, an "extended" form of
+WKT that includes the SRID::
+
+ >>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')
+
+When using GeoDjango's ORM, it will automatically wrap geometry values
+in transformation SQL, allowing the developer to work at a higher level
+of abstraction::
+
+ >>> qs = WorldBorders.objects.filter(mpoly__intersects=pnt)
+ >>> qs.query.as_sql() # Generating the SQL
+ ('SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area",
+ "world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2",
+ "world_worldborders"."iso3", "world_worldborders"."un", "world_worldborders"."region",
+ "world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat",
+ "world_worldborders"."mpoly" FROM "world_worldborders"
+ WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326))',
+ (<django.contrib.gis.db.backend.postgis.adaptor.PostGISAdaptor object at 0x25641b0>,))
+ >>> qs # printing evaluates the queryset
+ [<WorldBorders: United States>]
+
+__ http://spatialreference.org/ref/epsg/32140/
+
+Lazy Geometries
+---------------
+Geometries come to GeoDjango in a standardized textual representation. Upon
+access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`,
+exposing powerful functionality, such as serialization properties for
+popular geospatial formats::
+
+ >>> sm = WorldBorders.objects.get(name='San Marino')
+ >>> sm.mpoly
+ <MultiPolygon object at 0x24c6798>
+ >>> sm.mpoly.wkt # WKT
+ MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
+ >>> sm.mpoly.wkb # WKB (as Python binary buffer)
+ <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
+ >>> sm.mpoly.geojson # GeoJSON (requires GDAL)
+ '{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
+
+This includes access to all of the advanced geometric operations provided by
+the GEOS library::
+
+ >>> pnt = Point(12.4604, 43.9420)
+ >>> sm.mpoly.contains(pnt)
+ True
+ >>> pnt.contains(sm.mpoly)
+ False
+
+``GeoQuerySet`` Methods
+-----------------------
+
+
+Putting your data on the map
+============================
+
+Google
+------
+
+Geographic Admin
+----------------
+
+GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>`
+to enable support for editing geometry fields.
+
+Basics
+^^^^^^
+
+GeoDjango also supplements the Django admin by allowing users to create
+and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_).
+
+Let's dive in again -- create a file called ``admin.py`` inside the
+``world`` application, and insert the following::
+
+ from django.contrib.gis import admin
+ from models import WorldBorders
+
+ admin.site.register(WorldBorders, admin.GeoModelAdmin)
+
+Next, edit your ``urls.py`` in the ``geodjango`` project folder to look
+as follows::
+
+ from django.conf.urls.defaults import *
+ from django.contrib.gis import admin
+
+ admin.autodiscover()
+
+ urlpatterns = patterns('',
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+Start up the Django development server::
+
+ $ python manage.py runserver
+
+Finally, browse to ``http://localhost:8000/admin/``, and log in with the admin
+user created after running ``syncdb``. Browse to any of the ``WorldBorders``
+entries -- the borders may be edited by clicking on a polygon and dragging
+the vertexes to the desired position.
+
+.. _OpenLayers: http://openlayers.org/
+.. _Open Street Map: http://openstreetmap.org/
+.. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html
+.. _Metacarta: http://metacarta.com
+
+.. _osmgeoadmin-intro:
+
+``OSMGeoAdmin``
+^^^^^^^^^^^^^^^
+
+With the :class:`~django.contrib.gis.admin.OSMGeoAdmin`, GeoDjango uses
+a `Open Street Map`_ layer in the admin.
+This provides more context (including street and thoroughfare details) than
+available with the :class:`~django.contrib.gis.admin.GeoModelAdmin`
+(which uses the `Vector Map Level 0`_ WMS data set hosted at `Metacarta`_).
+
+First, there are some important requirements and limitations:
+
+* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the
+ :ref:`spherical mercator projection be added <addgoogleprojection>`
+ to the to be added to the ``spatial_ref_sys`` table (PostGIS 1.3 and
+ below, only).
+* The PROJ.4 datum shifting files must be installed (see the
+ :ref:`PROJ.4 installation instructions <proj4>` for more details).
+
+If you meet these requirements, then just substitute in the ``OSMGeoAdmin``
+option class in your ``admin.py`` file::
+
+ admin.site.register(WorldBorders, admin.OSMGeoAdmin)
+
+.. rubric:: Footnotes
+
+.. [#] Special thanks to Bjørn Sandvik of `thematicmapping.org <http://thematicmapping.org>`_ for providing and maintaining this data set.
+.. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and Christopher Schmidt.
+.. [#] Here the point is for the `University of Houston Law Center <http://www.law.uh.edu/>`_ .
+.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049.
diff --git a/parts/django/docs/ref/contrib/gis/utils.txt b/parts/django/docs/ref/contrib/gis/utils.txt
new file mode 100644
index 0000000..9f8e518
--- /dev/null
+++ b/parts/django/docs/ref/contrib/gis/utils.txt
@@ -0,0 +1,32 @@
+.. _ref-gis-utils:
+
+===================
+GeoDjango Utilities
+===================
+
+.. module:: django.contrib.gis.utils
+ :synopsis: GeoDjango's collection of utilities.
+
+The :mod:`django.contrib.gis.utils` module contains various utilities that are
+useful in creating geospatial Web applications.
+
+.. toctree::
+ :maxdepth: 2
+
+ geoip
+ layermapping
+ ogrinspect
+
+GeoIP
+=====
+
+Interface to the MaxMind GeoIP library for performing IP-based geolocation
+from GeoDjango. See :ref:`GeoIP reference <ref-geoip>` documentation for
+more information.
+
+LayerMapping
+============
+
+The :class:`~django.contrib.gis.utils.LayerMapping` simplifies the process
+of importing spatial data and attributes into your GeoDjango models.
+
diff --git a/parts/django/docs/ref/contrib/humanize.txt b/parts/django/docs/ref/contrib/humanize.txt
new file mode 100644
index 0000000..b5ec518
--- /dev/null
+++ b/parts/django/docs/ref/contrib/humanize.txt
@@ -0,0 +1,100 @@
+========================
+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 the following
+filters.
+
+.. templatefilter:: apnumber
+
+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.
+
+.. templatefilter:: intcomma
+
+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.
+
+.. templatefilter:: intword
+
+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.
+
+.. templatefilter:: naturalday
+
+naturalday
+----------
+
+.. versionadded:: 1.0
+
+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 :tfilter:`date` 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.
+
+.. templatefilter:: ordinal
+
+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.
diff --git a/parts/django/docs/ref/contrib/index.txt b/parts/django/docs/ref/contrib/index.txt
new file mode 100644
index 0000000..90edf72
--- /dev/null
+++ b/parts/django/docs/ref/contrib/index.txt
@@ -0,0 +1,207 @@
+====================
+``contrib`` packages
+====================
+
+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/tutorial/stdlib.html#batteries-included
+
+.. toctree::
+ :maxdepth: 1
+
+ admin/index
+ auth
+ comments/index
+ contenttypes
+ csrf
+ databrowse
+ flatpages
+ formtools/index
+ gis/index
+ humanize
+ localflavor
+ markup
+ messages
+ redirects
+ sitemaps
+ sites
+ syndication
+ webdesign
+
+admin
+=====
+
+The automatic Django administrative interface. For more information, see
+:doc:`Tutorial 2 </intro/tutorial02>` and the
+:doc:`admin documentation </ref/contrib/admin/index>`.
+
+Requires the auth_ and contenttypes_ contrib packages to be installed.
+
+auth
+====
+
+Django's authentication framework.
+
+See :doc:`/topics/auth`.
+
+comments
+========
+
+.. versionchanged:: 1.0
+ The comments application has been rewriten. See :doc:`/ref/contrib/comments/upgrade`
+ for information on howto upgrade.
+
+A simple yet flexible comments system. See :doc:`/ref/contrib/comments/index`.
+
+contenttypes
+============
+
+A light framework for hooking into "types" of content, where each installed
+Django model is a separate content type.
+
+See the :doc:`contenttypes documentation </ref/contrib/contenttypes>`.
+
+csrf
+====
+
+A middleware for preventing Cross Site Request Forgeries
+
+See the :doc:`csrf documentation </ref/contrib/csrf>`.
+
+flatpages
+=========
+
+A framework for managing simple "flat" HTML content in a database.
+
+See the :doc:`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 :doc:`form preview documentation </ref/contrib/formtools/form-preview>`.
+
+django.contrib.formtools.wizard
+--------------------------------
+
+Splits forms across multiple Web pages.
+
+See the :doc:`form wizard documentation </ref/contrib/formtools/form-wizard>`.
+
+gis
+====
+
+A world-class geospatial framework built on top of Django, that enables
+storage, manipulation and display of spatial data.
+
+See the :doc:`/ref/contrib/gis/index` documentation for more.
+
+humanize
+========
+
+A set of Django template filters useful for adding a "human touch" to data.
+
+See the :doc:`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 :doc:`localflavor documentation </ref/contrib/localflavor>`.
+
+markup
+======
+
+A collection of template filters that implement common markup languages
+
+See the :doc:`markup documentation </ref/contrib/markup>`.
+
+messages
+========
+
+.. versionchanged:: 1.2
+ The messages framework was added.
+
+A framework for storing and retrieving temporary cookie- or session-based
+messages
+
+See the :doc:`messages documentation </ref/contrib/messages>`.
+
+redirects
+=========
+
+A framework for managing redirects.
+
+See the :doc:`redirects documentation </ref/contrib/redirects>`.
+
+sessions
+========
+
+A framework for storing data in anonymous sessions.
+
+See the :doc:`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 :doc:`sites documentation </ref/contrib/sites>`.
+
+sitemaps
+========
+
+A framework for generating Google sitemap XML files.
+
+See the :doc:`sitemaps documentation </ref/contrib/sitemaps>`.
+
+syndication
+===========
+
+A framework for generating syndication feeds, in RSS and Atom, quite easily.
+
+See the :doc:`syndication documentation </ref/contrib/syndication>`.
+
+webdesign
+=========
+
+Helpers and utilities targeted primarily at Web *designers* rather than
+Web *developers*.
+
+See the :doc:`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
diff --git a/parts/django/docs/ref/contrib/localflavor.txt b/parts/django/docs/ref/contrib/localflavor.txt
new file mode 100644
index 0000000..2eb731d
--- /dev/null
+++ b/parts/django/docs/ref/contrib/localflavor.txt
@@ -0,0 +1,842 @@
+==========================
+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 :doc:`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.fr.forms import FRPhoneNumberField
+
+ class MyForm(forms.Form):
+ my_french_phone_no = FRPhoneNumberField()
+
+Supported countries
+===================
+
+Countries currently supported by :mod:`~django.contrib.localflavor` are:
+
+ * Argentina_
+ * Australia_
+ * Austria_
+ * Brazil_
+ * Canada_
+ * Chile_
+ * Czech_
+ * Finland_
+ * France_
+ * Germany_
+ * Iceland_
+ * India_
+ * Indonesia_
+ * Ireland_
+ * Italy_
+ * Japan_
+ * Kuwait_
+ * Mexico_
+ * `The Netherlands`_
+ * Norway_
+ * Peru_
+ * Poland_
+ * Portugal_
+ * Romania_
+ * Slovakia_
+ * `South Africa`_
+ * Spain_
+ * Sweden_
+ * Switzerland_
+ * `United Kingdom`_
+ * `United States of America`_
+ * Uruguay_
+
+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, datetime and split datetime input fields based on
+those from :doc:`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)`_
+.. _Czech: `Czech (cz)`_
+.. _Finland: `Finland (fi)`_
+.. _France: `France (fr)`_
+.. _Germany: `Germany (de)`_
+.. _The Netherlands: `The Netherlands (nl)`_
+.. _Iceland: `Iceland (is\_)`_
+.. _India: `India (in\_)`_
+.. _Indonesia: `Indonesia (id)`_
+.. _Ireland: `Ireland (ie)`_
+.. _Italy: `Italy (it)`_
+.. _Japan: `Japan (jp)`_
+.. _Kuwait: `Kuwait (kw)`_
+.. _Mexico: `Mexico (mx)`_
+.. _Norway: `Norway (no)`_
+.. _Peru: `Peru (pe)`_
+.. _Poland: `Poland (pl)`_
+.. _Portugal: `Portugal (pt)`_
+.. _Romania: `Romania (ro)`_
+.. _Slovakia: `Slovakia (sk)`_
+.. _South Africa: `South Africa (za)`_
+.. _Spain: `Spain (es)`_
+.. _Sweden: `Sweden (se)`_
+.. _Switzerland: `Switzerland (ch)`_
+.. _United Kingdom: `United Kingdom (uk)`_
+.. _United States of America: `United States of America (us)`_
+.. _Uruguay: `Uruguay (uy)`_
+
+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.
+
+.. class:: br.forms.BRCPFField
+
+ A form field that validates input as `Brazilian CPF`_.
+
+ Input can either be of the format XXX.XXX.XXX-VD or be a group of 11 digits.
+
+.. _Brazilian CPF: http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
+
+.. class:: br.forms.BRCNPJField
+
+ A form field that validates input as `Brazilian CNPJ`_.
+
+ Input can either be of the format XX.XXX.XXX/XXXX-XX or be a group of 14
+ digits.
+
+.. _Brazilian CNPJ: http://en.wikipedia.org/wiki/National_identification_number#Brazil
+
+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.
+
+Czech (``cz``)
+==============
+
+.. class:: cz.forms.CZPostalCodeField
+
+ A form field that validates input as a Czech postal code. Valid formats
+ are XXXXX or XXX XX, where X is a digit.
+
+.. class:: cz.forms.CZBirthNumberField
+
+ A form field that validates input as a Czech Birth Number.
+ A valid number must be in format XXXXXX/XXXX (slash is optional).
+
+.. class:: cz.forms.CZICNumberField
+
+ A form field that validates input as a Czech IC number field.
+
+.. class:: cz.forms.CZRegionSelect
+
+ A ``Select`` widget that uses a list of Czech regions 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.
+
+The Netherlands (``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.
+
+Ireland (``ie``)
+================
+
+.. class:: ie.forms.IECountySelect
+
+ A ``Select`` widget that uses a list of Irish Counties as its choices.
+
+Indonesia (``id``)
+==================
+
+.. class:: id.forms.IDPostCodeField
+
+ A form field that validates input as an Indonesian post code field.
+
+.. class:: id.forms.IDProvinceSelect
+
+ A ``Select`` widget that uses a list of Indonesian provinces as its choices.
+
+.. class:: id.forms.IDPhoneNumberField
+
+ A form field that validates input as an Indonesian telephone number.
+
+.. class:: id.forms.IDLicensePlatePrefixSelect
+
+ A ``Select`` widget that uses a list of Indonesian license plate
+ prefix code as its choices.
+
+.. class:: id.forms.IDLicensePlateField
+
+ A form field that validates input as an Indonesian vehicle license plate.
+
+.. class:: id.forms.IDNationalIdentityNumberField
+
+ A form field that validates input as an Indonesian national identity
+ number (`NIK`_/KTP). The output will be in the format of
+ 'XX.XXXX.DDMMYY.XXXX'. Dots or spaces can be used in the input to break
+ down the numbers.
+
+.. _NIK: http://en.wikipedia.org/wiki/Indonesian_identity_card
+
+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/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.
+
+Kuwait (``kw``)
+===============
+
+.. class:: kw.forms.KWCivilIDNumberField
+
+ A form field that validates input as a Kuwaiti Civil ID number. A valid
+ Civil ID number must obey the following rules:
+
+ * The number consist of 12 digits.
+ * The birthdate of the person is a valid date.
+ * The calculated checksum equals to the last digit of the Civil ID.
+
+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:: pe.forms.PEDNIField
+
+ A form field that validates input as a DNI (Peruvian national identity)
+ number.
+
+.. class:: pe.forms.PERUCField
+
+ A form field that validates input as an RUC (Registro Unico de
+ Contribuyentes) number. Valid RUC numbers have 11 digits.
+
+.. class:: pe.forms.PEDepartmentSelect
+
+ A ``Select`` widget that uses a list of Peruvian Departments as its choices.
+
+Poland (``pl``)
+===============
+
+.. class:: pl.forms.PLPESELField
+
+ A form field that validates input as a Polish national identification number
+ (PESEL_).
+
+.. _PESEL: http://en.wikipedia.org/wiki/PESEL
+
+.. class:: pl.forms.PLREGONField
+
+ 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.PLNIPField
+
+ 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.PLCountySelect
+
+ A ``Select`` widget that uses a list of Polish administrative units as its
+ choices.
+
+.. class:: pl.forms.PLProvinceSelect
+
+ A ``Select`` widget that uses a list of Polish voivodeships (administrative
+ provinces) as its choices.
+
+Portugal (``pt``)
+=================
+
+.. class:: pt.forms.PTZipCodeField
+
+ A form field that validates input as a Portuguese zip code.
+
+.. class:: pt.forms.PTPhoneNumberField
+
+ A form field that validates input as a Portuguese phone number.
+ Valid numbers have 9 digits (may include spaces) or start by 00
+ or + (international).
+
+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.
+
+Sweden (``se``)
+===============
+
+.. class:: se.forms.SECountySelect
+
+ A Select form widget that uses a list of the Swedish counties (län) as its
+ choices.
+
+ The cleaned value is the official county code -- see
+ http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
+
+.. class:: se.forms.SEOrganisationNumber
+
+ A form field that validates input as a Swedish organisation number
+ (organisationsnummer).
+
+ It accepts the same input as SEPersonalIdentityField (for sole
+ proprietorships (enskild firma). However, co-ordination numbers are not
+ accepted.
+
+ It also accepts ordinary Swedish organisation numbers with the format
+ NNNNNNNNNN.
+
+ The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
+ for other organisations.
+
+.. class:: se.forms.SEPersonalIdentityNumber
+
+ A form field that validates input as a Swedish personal identity number
+ (personnummer).
+
+ The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
+ YYMMDDXXXX and YYMMDD+XXXX.
+
+ A \+ indicates that the person is older than 100 years, which will be taken
+ into consideration when the date is validated.
+
+ The checksum will be calculated and checked. The birth date is checked
+ to be a valid date.
+
+ By default, co-ordination numbers (samordningsnummer) will be accepted. To
+ only allow real personal identity numbers, pass the keyword argument
+ coordination_number=False to the constructor.
+
+ The cleaned value will always have the format YYYYMMDDXXXX.
+
+.. class:: se.forms.SEPostalCodeField
+
+ A form field that validates input as a Swedish postal code (postnummer).
+ Valid codes consist of five digits (XXXXX). The number can optionally be
+ formatted with a space after the third digit (XXX XX).
+
+ The cleaned value will never contain the space.
+
+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.cabinetoffice.gov.uk/media/291293/bs7666-v2-0.xml.
+
+.. class:: uk.forms.UKCountySelect
+
+ A ``Select`` widget that uses a list of UK counties/regions as its choices.
+
+.. class:: uk.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.
+
+.. class:: us.models.PhoneNumberField
+
+ A :class:`CharField` that checks that the value is a valid U.S.A.-style phone
+ number (in the format ``XXX-XXX-XXXX``).
+
+.. class:: us.models.USStateField
+
+ A model field that forms represent as a ``forms.USStateField`` field and
+ stores the two-letter U.S. state abbreviation in the database.
+
+Uruguay (``uy``)
+================
+
+.. class:: uy.forms.UYCIField
+
+ A field that validates Uruguayan 'Cedula de identidad' (CI) numbers.
+
+.. class:: uy.forms.UYDepartamentSelect
+
+ A ``Select`` widget that uses a list of Uruguayan departaments as its
+ choices.
diff --git a/parts/django/docs/ref/contrib/markup.txt b/parts/django/docs/ref/contrib/markup.txt
new file mode 100644
index 0000000..9282313
--- /dev/null
+++ b/parts/django/docs/ref/contrib/markup.txt
@@ -0,0 +1,42 @@
+=====================
+django.contrib.markup
+=====================
+
+.. module:: django.contrib.markup
+ :synopsis: A collection of template filters that implement common markup languages.
+
+Django provides template filters that implement the following markup
+languages:
+
+ * ``textile`` -- implements `Textile`_ -- requires `PyTextile`_
+ * ``markdown`` -- implements `Markdown`_ -- requires `Python-markdown`_
+ * ``restructuredtext`` -- implements `reST (reStructured Text)`_
+ -- requires `doc-utils`_
+
+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
+:file:`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
+.. _PyTextile: http://loopcore.com/python-textile/
+.. _Python-markdown: http://www.freewisdom.org/projects/python-markdown
+.. _doc-utils: http://docutils.sf.net/
+
+reStructured Text
+-----------------
+
+When using the ``restructuredtext`` markup filter you can define a
+:setting:`RESTRUCTUREDTEXT_FILTER_SETTINGS` in your django settings to
+override the default writer settings. See the `restructuredtext writer
+settings`_ for details on what these settings are.
+
+.. _restructuredtext writer settings: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer
diff --git a/parts/django/docs/ref/contrib/messages.txt b/parts/django/docs/ref/contrib/messages.txt
new file mode 100644
index 0000000..3081f27
--- /dev/null
+++ b/parts/django/docs/ref/contrib/messages.txt
@@ -0,0 +1,411 @@
+======================
+The messages framework
+======================
+
+.. module:: django.contrib.messages
+ :synopsis: Provides cookie- and session-based temporary message storage.
+
+Django provides full support for cookie- and session-based messaging, for
+both anonymous and authenticated clients. The messages framework allows you
+to temporarily store messages in one request and retrieve them for display
+in a subsequent request (usually the next one). Every message is tagged
+with a specific ``level`` that determines its priority (e.g., ``info``,
+``warning``, or ``error``).
+
+.. versionadded:: 1.2
+ The messages framework was added.
+
+Enabling messages
+=================
+
+Messages are implemented through a :doc:`middleware </ref/middleware>`
+class and corresponding :doc:`context processor </ref/templates/api>`.
+
+To enable message functionality, do the following:
+
+ * Edit the :setting:`MIDDLEWARE_CLASSES` setting and make sure
+ it contains ``'django.contrib.messages.middleware.MessageMiddleware'``.
+
+ If you are using a :ref:`storage backend <message-storage-backends>` that
+ relies on :doc:`sessions </topics/http/sessions>` (the default),
+ ``'django.contrib.sessions.middleware.SessionMiddleware'`` must be
+ enabled and appear before ``MessageMiddleware`` in your
+ :setting:`MIDDLEWARE_CLASSES`.
+
+ * Edit the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting and make sure
+ it contains ``'django.contrib.messages.context_processors.messages'``.
+
+ * Add ``'django.contrib.messages'`` to your :setting:`INSTALLED_APPS`
+ setting
+
+The default ``settings.py`` created by ``django-admin.py startproject`` has
+``MessageMiddleware`` activated and the ``django.contrib.messages`` app
+installed. Also, the default value for :setting:`TEMPLATE_CONTEXT_PROCESSORS`
+contains ``'django.contrib.messages.context_processors.messages'``.
+
+If you don't want to use messages, you can remove the
+``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, the ``messages``
+context processor from :setting:`TEMPLATE_CONTEXT_PROCESSORS` and
+``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`.
+
+Configuring the message engine
+==============================
+
+.. _message-storage-backends:
+
+Storage backends
+----------------
+
+The messages framework can use different backends to store temporary messages.
+To change which backend is being used, add a `MESSAGE_STORAGE`_ to your
+settings, referencing the module and class of the storage class. For
+example::
+
+ MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
+
+The value should be the full path of the desired storage class.
+
+Four storage classes are included:
+
+``'django.contrib.messages.storage.session.SessionStorage'``
+ This class stores all messages inside of the request's session. It
+ requires Django's ``contrib.sessions`` application.
+
+``'django.contrib.messages.storage.cookie.CookieStorage'``
+ This class stores the message data in a cookie (signed with a secret hash
+ to prevent manipulation) to persist notifications across requests. Old
+ messages are dropped if the cookie data size would exceed 4096 bytes.
+
+``'django.contrib.messages.storage.fallback.FallbackStorage'``
+ This class first uses CookieStorage for all messages, falling back to using
+ SessionStorage for the messages that could not fit in a single cookie.
+
+ Since it is uses SessionStorage, it also requires Django's
+ ``contrib.session`` application.
+
+``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
+ This is the default temporary storage class.
+
+ This class extends FallbackStorage and adds compatibility methods to
+ to retrieve any messages stored in the user Message model by code that
+ has not yet been updated to use the new API. This storage is temporary
+ (because it makes use of code that is pending deprecation) and will be
+ removed in Django 1.4. At that time, the default storage will become
+ ``django.contrib.messages.storage.fallback.FallbackStorage``. For more
+ information, see `LegacyFallbackStorage`_ below.
+
+To write your own storage class, subclass the ``BaseStorage`` class in
+``django.contrib.messages.storage.base`` and implement the ``_get`` and
+``_store`` methods.
+
+LegacyFallbackStorage
+^^^^^^^^^^^^^^^^^^^^^
+
+The ``LegacyFallbackStorage`` is a temporary tool to facilitate the transition
+from the deprecated ``user.message_set`` API and will be removed in Django 1.4
+according to Django's standard deprecation policy. For more information, see
+the full :doc:`release process documentation </internals/release-process>`.
+
+In addition to the functionality in the ``FallbackStorage``, it adds a custom,
+read-only storage class that retrieves messages from the user ``Message``
+model. Any messages that were stored in the ``Message`` model (e.g., by code
+that has not yet been updated to use the messages framework) will be retrieved
+first, followed by those stored in a cookie and in the session, if any. Since
+messages stored in the ``Message`` model do not have a concept of levels, they
+will be assigned the ``INFO`` level by default.
+
+Message levels
+--------------
+
+The messages framework is based on a configurable level architecture similar
+to that of the Python logging module. Message levels allow you to group
+messages by type so they can be filtered or displayed differently in views and
+templates.
+
+The built-in levels (which can be imported from ``django.contrib.messages``
+directly) are:
+
+=========== ========
+Constant Purpose
+=========== ========
+``DEBUG`` Development-related messages that will be ignored (or removed) in a production deployment
+``INFO`` Informational messages for the user
+``SUCCESS`` An action was successful, e.g. "Your profile was updated successfully"
+``WARNING`` A failure did not occur but may be imminent
+``ERROR`` An action was **not** successful or some other failure occurred
+=========== ========
+
+The `MESSAGE_LEVEL`_ setting can be used to change the minimum recorded level
+(or it can be `changed per request`_). Attempts to add messages of a level less
+than this will be ignored.
+
+.. _`changed per request`: `Changing the minimum recorded level per-request`_
+
+Message tags
+------------
+
+Message tags are a string representation of the message level plus any
+extra tags that were added directly in the view (see
+`Adding extra message tags`_ below for more details). Tags are stored in a
+string and are separated by spaces. Typically, message tags
+are used as CSS classes to customize message style based on message type. By
+default, each level has a single tag that's a lowercase version of its own
+constant:
+
+============== ===========
+Level Constant Tag
+============== ===========
+``DEBUG`` ``debug``
+``INFO`` ``info``
+``SUCCESS`` ``success``
+``WARNING`` ``warning``
+``ERROR`` ``error``
+============== ===========
+
+To change the default tags for a message level (either built-in or custom),
+set the `MESSAGE_TAGS`_ setting to a dictionary containing the levels
+you wish to change. As this extends the default tags, you only need to provide
+tags for the levels you wish to override::
+
+ from django.contrib.messages import constants as messages
+ MESSAGE_TAGS = {
+ messages.INFO: '',
+ 50: 'critical',
+ }
+
+Using messages in views and templates
+=====================================
+
+Adding a message
+----------------
+
+To add a message, call::
+
+ from django.contrib import messages
+ messages.add_message(request, messages.INFO, 'Hello world.')
+
+Some shortcut methods provide a standard way to add messages with commonly
+used tags (which are usually represented as HTML classes for the message)::
+
+ messages.debug(request, '%s SQL statements were executed.' % count)
+ messages.info(request, 'Three credits remain in your account.')
+ messages.success(request, 'Profile details updated.')
+ messages.warning(request, 'Your account expires in three days.')
+ messages.error(request, 'Document deleted.')
+
+Displaying messages
+-------------------
+
+In your template, use something like::
+
+ {% if messages %}
+ <ul class="messages">
+ {% for message in messages %}
+ <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+If you're using the context processor, your template should be rendered with a
+``RequestContext``. Otherwise, ensure ``messages`` is available to
+the template context.
+
+Creating custom message levels
+------------------------------
+
+Messages levels are nothing more than integers, so you can define your own
+level constants and use them to create more customized user feedback, e.g.::
+
+ CRITICAL = 50
+
+ def my_view(request):
+ messages.add_message(request, CRITICAL, 'A serious error occurred.')
+
+When creating custom message levels you should be careful to avoid overloading
+existing levels. The values for the built-in levels are:
+
+.. _message-level-constants:
+
+============== =====
+Level Constant Value
+============== =====
+``DEBUG`` 10
+``INFO`` 20
+``SUCCESS`` 25
+``WARNING`` 30
+``ERROR`` 40
+============== =====
+
+If you need to identify the custom levels in your HTML or CSS, you need to
+provide a mapping via the `MESSAGE_TAGS`_ setting.
+
+.. note::
+ If you are creating a reusable application, it is recommended to use
+ only the built-in `message levels`_ and not rely on any custom levels.
+
+Changing the minimum recorded level per-request
+-----------------------------------------------
+
+The minimum recorded level can be set per request via the ``set_level``
+method::
+
+ from django.contrib import messages
+
+ # Change the messages level to ensure the debug message is added.
+ messages.set_level(request, messages.DEBUG)
+ messages.debug(request, 'Test message...')
+
+ # In another request, record only messages with a level of WARNING and higher
+ messages.set_level(request, messages.WARNING)
+ messages.success(request, 'Your profile was updated.') # ignored
+ messages.warning(request, 'Your account is about to expire.') # recorded
+
+ # Set the messages level back to default.
+ messages.set_level(request, None)
+
+Similarly, the current effective level can be retrieved with ``get_level``::
+
+ from django.contrib import messages
+ current_level = messages.get_level(request)
+
+For more information on how the minimum recorded level functions, see
+`Message levels`_ above.
+
+Adding extra message tags
+-------------------------
+
+For more direct control over message tags, you can optionally provide a string
+containing extra tags to any of the add methods::
+
+ messages.add_message(request, messages.INFO, 'Over 9000!',
+ extra_tags='dragonball')
+ messages.error(request, 'Email box full', extra_tags='email')
+
+Extra tags are added before the default tag for that level and are space
+separated.
+
+Failing silently when the message framework is disabled
+-------------------------------------------------------
+
+If you're writing a reusable app (or other piece of code) and want to include
+messaging functionality, but don't want to require your users to enable it
+if they don't want to, you may pass an additional keyword argument
+``fail_silently=True`` to any of the ``add_message`` family of methods. For
+example::
+
+ messages.add_message(request, messages.SUCCESS, 'Profile details updated.',
+ fail_silently=True)
+ messages.info(request, 'Hello world.', fail_silently=True)
+
+Internally, Django uses this functionality in the create, update, and delete
+:doc:`generic views </topics/http/generic-views>` so that they work even if the
+message framework is disabled.
+
+.. note::
+ Setting ``fail_silently=True`` only hides the ``MessageFailure`` that would
+ otherwise occur when the messages framework disabled and one attempts to
+ use one of the ``add_message`` family of methods. It does not hide failures
+ that may occur for other reasons.
+
+Expiration of messages
+======================
+
+The messages are marked to be cleared when the storage instance is iterated
+(and cleared when the response is processed).
+
+To avoid the messages being cleared, you can set the messages storage to
+``False`` after iterating::
+
+ storage = messages.get_messages(request)
+ for message in storage:
+ do_something_with(message)
+ storage.used = False
+
+Behavior of parallel requests
+=============================
+
+Due to the way cookies (and hence sessions) work, **the behavior of any
+backends that make use of cookies or sessions is undefined when the same
+client makes multiple requests that set or get messages in parallel**. For
+example, if a client initiates a request that creates a message in one window
+(or tab) and then another that fetches any uniterated messages in another
+window, before the first window redirects, the message may appear in the
+second window instead of the first window where it may be expected.
+
+In short, when multiple simultaneous requests from the same client are
+involved, messages are not guaranteed to be delivered to the same window that
+created them nor, in some cases, at all. Note that this is typically not a
+problem in most applications and will become a non-issue in HTML5, where each
+window/tab will have its own browsing context.
+
+Settings
+========
+
+A few :doc:`Django settings </ref/settings>` give you control over message
+behavior:
+
+MESSAGE_LEVEL
+-------------
+
+Default: ``messages.INFO``
+
+This sets the minimum message that will be saved in the message storage. See
+`Message levels`_ above for more details.
+
+.. admonition:: Important
+
+ If you override ``MESSAGE_LEVEL`` in your settings file and rely on any of
+ the built-in constants, you must import the constants module directly to
+ avoid the potential for circular imports, e.g.::
+
+ from django.contrib.messages import constants as message_constants
+ MESSAGE_LEVEL = message_constants.DEBUG
+
+ If desired, you may specify the numeric values for the constants directly
+ according to the values in the above :ref:`constants table
+ <message-level-constants>`.
+
+MESSAGE_STORAGE
+---------------
+
+Default: ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
+
+Controls where Django stores message data. Valid values are:
+
+ * ``'django.contrib.messages.storage.fallback.FallbackStorage'``
+ * ``'django.contrib.messages.storage.session.SessionStorage'``
+ * ``'django.contrib.messages.storage.cookie.CookieStorage'``
+ * ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
+
+See `Storage backends`_ for more details.
+
+MESSAGE_TAGS
+------------
+
+Default::
+
+ {messages.DEBUG: 'debug',
+ messages.INFO: 'info',
+ messages.SUCCESS: 'success',
+ messages.WARNING: 'warning',
+ messages.ERROR: 'error',}
+
+This sets the mapping of message level to message tag, which is typically
+rendered as a CSS class in HTML. If you specify a value, it will extend
+the default. This means you only have to specify those values which you need
+to override. See `Displaying messages`_ above for more details.
+
+.. admonition:: Important
+
+ If you override ``MESSAGE_TAGS`` in your settings file and rely on any of
+ the built-in constants, you must import the ``constants`` module directly to
+ avoid the potential for circular imports, e.g.::
+
+ from django.contrib.messages import constants as message_constants
+ MESSAGE_TAGS = {message_constants.INFO: ''}
+
+ If desired, you may specify the numeric values for the constants directly
+ according to the values in the above :ref:`constants table
+ <message-level-constants>`.
+
+.. _Django settings: ../settings/
diff --git a/parts/django/docs/ref/contrib/redirects.txt b/parts/django/docs/ref/contrib/redirects.txt
new file mode 100644
index 0000000..f1a58cb
--- /dev/null
+++ b/parts/django/docs/ref/contrib/redirects.txt
@@ -0,0 +1,70 @@
+=================
+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 :doc:`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 :doc:`Django model </topics/db/models>`,
+ which lives in `django/contrib/redirects/models.py`_. You can access redirect
+ objects via the :doc:`Django database API </topics/db/queries>`.
+
+.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
diff --git a/parts/django/docs/ref/contrib/sitemaps.txt b/parts/django/docs/ref/contrib/sitemaps.txt
new file mode 100644
index 0000000..eb29c6c
--- /dev/null
+++ b/parts/django/docs/ref/contrib/sitemaps.txt
@@ -0,0 +1,351 @@
+=====================
+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 :doc:`syndication framework
+</ref/contrib/syndication>`. To create a sitemap, just write a
+:class:`~django.contrib.sitemaps.Sitemap` class and point to it in your
+:doc:`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.Loader'``
+ 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.Loader` template
+loader can find the default templates.)
+
+Initialization
+==============
+
+To activate sitemap generation on your Django site, add this line to your
+:doc:`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 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 path for a given object
+ as returned by :attr:`~Sitemap.items()`.
+
+ If it's an attribute, its value should be a string representing an
+ absolute path to use for *every* object returned by
+ :attr:`~Sitemap.items()`.
+
+ In both cases, "absolute path" 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
+ publicly visible :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
+ :doc:`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 :doc:`URLconf </topics/http/urls>` using both::
+
+ from django.conf.urls.defaults import *
+ from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
+ from 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's 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.
+
+You should create an index file if one of your sitemaps has more than 50,000
+URLs. In this case, Django will automatically paginate the sitemap, 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 sitemaps 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 path to 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.
+
+.. admonition:: Register with Google first!
+
+ The :func:`ping_google` command only works if you have registered your
+ site with `Google Webmaster Tools`_.
+
+.. _`Google Webmaster Tools`: http://www.google.com/webmasters/tools/
+
+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, force_insert=False, force_update=False):
+ super(Entry, self).save(force_insert, force_update)
+ 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`
+------------------------------
+
+.. django-admin:: ping_google
+
+.. versionadded:: 1.0
+
+Once the sitemaps application is added to your project, you may also
+ping Google using the ``ping_google`` management command::
+
+ python manage.py ping_google [/sitemap.xml]
diff --git a/parts/django/docs/ref/contrib/sites.txt b/parts/django/docs/ref/contrib/sites.txt
new file mode 100644
index 0000000..6d795d0
--- /dev/null
+++ b/parts/django/docs/ref/contrib/sites.txt
@@ -0,0 +1,415 @@
+=====================
+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
+----------------------------------------
+
+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.
+
+.. versionchanged:: 1.3
+
+For code which relies on getting the current domain but cannot be certain
+that the sites framework will be installed for any given project, there is a
+utility function :func:`~django.contrib.sites.models.get_current_site` that
+takes a request object as an argument and returns either a Site instance (if
+the sites framework is installed) or a RequestSite instance (if it is not).
+This allows loose coupling with the sites framework and provides a usable
+fallback for cases where it is not installed.
+
+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
+===================================
+
+.. versionadded:: 1.0
+
+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` plays a key role in your
+application, consider using the helpful
+:class:`~django.contrib.sites.managers.CurrentSiteManager` in your
+model(s). It's a model :doc:`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`? By default,
+:class:`~django.contrib.sites.managers.CurrentSiteManager` looks for a
+either a :class:`~django.db.models.fields.related.ForeignKey` called
+``site`` or a
+:class:`~django.db.models.fields.related.ManyToManyField` called
+``sites`` to filter on. If you use a field named something other than
+``site`` or ``sites`` to identify which
+:class:`~django.contrib.sites.models.Site` objects your object is
+related to, then you need to explicitly pass the custom field name as
+a parameter to
+:class:`~django.contrib.sites.managers.CurrentSiteManager` on your
+model. 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 :doc:`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:
+
+.. versionadded:: 1.0
+
+Some :doc:`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`.
diff --git a/parts/django/docs/ref/contrib/syndication.txt b/parts/django/docs/ref/contrib/syndication.txt
new file mode 100644
index 0000000..04f14b5
--- /dev/null
+++ b/parts/django/docs/ref/contrib/syndication.txt
@@ -0,0 +1,949 @@
+==============================
+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
+========================
+
+.. versionchanged:: 1.2
+ The high-level feeds framework was refactored in Django 1.2. The
+ pre-1.2 interface still exists, but it has been deprecated, and
+ will be removed in Django 1.4. If you need to maintain an old-style
+ Django feed, please consult the Django 1.1 documentation. For
+ details on updating to use the new high-level feed framework, see
+ the :ref:`Django 1.2 release notes <1.2-updating-feeds>`.
+
+Overview
+--------
+
+The high-level feed-generating framework is supplied by the
+:class:`~django.contrib.syndication.views.Feed` class. To create a
+feed, write a :class:`~django.contrib.syndication.views.Feed` class
+and point to an instance of it in your :doc:`URLconf
+</topics/http/urls>`.
+
+Feed classes
+------------
+
+A :class:`~django.contrib.syndication.views.Feed` class is a 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).
+
+Feed classes subclass :class:`django.contrib.syndication.views.Feed`.
+They can live anywhere in your codebase.
+
+Instances of :class:`~django.contrib.syndication.views.Feed` classes
+are views which can be used in your :doc:`URLconf </topics/http/urls>`.
+
+A simple example
+----------------
+
+This simple example, taken from `chicagocrime.org`_, describes a feed of the
+latest five news items::
+
+ from django.contrib.syndication.views import Feed
+ from chicagocrime.models import NewsItem
+
+ class LatestEntriesFeed(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]
+
+ def item_title(self, item):
+ return item.title
+
+ def item_description(self, item):
+ return item.description
+
+To connect a URL to this feed, put an instance of the Feed object in
+your :doc:`URLconf </topics/http/urls>`. For example::
+
+ from django.conf.urls.defaults import *
+ from myproject.feeds import LatestEntriesFeed
+
+ urlpatterns = patterns('',
+ # ...
+ (r'^latest/feed/$', LatestEntriesFeed()),
+ # ...
+ )
+
+Note:
+
+* The Feed class subclasses :class:`django.contrib.syndication.views.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
+ :doc:`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 is 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.
+
+ * For the contents of ``<title>`` and ``<description>``, Django tries
+ calling the methods :meth:`item_title()` and :meth:`item_description()` on
+ the :class:`~django.contrib.syndication.views.Feed` class. They are passed
+ a single parameter, :attr:`item`, which is the object itself. These are
+ optional; by default, the unicode representation of the object is used for
+ both.
+
+ If you want to do any special formatting for either the title or
+ description, :doc:`Django templates </topics/templates>` can be used
+ instead. Their paths can be specified with the ``title_template`` and
+ ``description_template`` attributes on the
+ :class:`~django.contrib.syndication.views.Feed` class. The templates are
+ rendered for each item and are passed 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 }}``. If you 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.
+
+ See `a complex example`_ below that uses a description template.
+
+ * To specify the contents of ``<link>``, you have two options. For each item
+ in :meth:`items()`, Django first tries calling the
+ :meth:`item_link()` method on the
+ :class:`~django.contrib.syndication.views.Feed` class. In a similar way to
+ the title and description, it is passed it a single parameter,
+ :attr:`item`. If that method doesn't exist, Django tries executing a
+ ``get_absolute_url()`` method on that object. Both
+ :meth:`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.
+
+.. _chicagocrime.org: http://www.chicagocrime.org/
+
+A complex example
+-----------------
+
+The framework also supports more complex feeds, via arguments.
+
+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.views.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 access the
+arguments passed from your :doc:`URLconf </topics/http/urls>` so feeds can output
+items based on information in the feed's URL.
+
+On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
+
+ * :file:`/beats/613/rss/` -- Returns recent crimes for beat 613.
+ * :file:`/beats/1424/rss/` -- Returns recent crimes for beat 1424.
+
+These can be matched with a :doc:`URLconf </topics/http/urls>` line such as::
+
+ (r'^beats/(?P<beat_id>\d+)/rss/$', BeatFeed()),
+
+Like a view, the arguments in the URL are passed to the :meth:`get_object()`
+method along with the request object.
+
+.. versionchanged:: 1.2
+ Prior to version 1.2, ``get_object()`` only accepted a ``bits`` argument.
+
+Here's the code for these beat-specific feeds::
+
+ from django.contrib.syndication.views import FeedDoesNotExist
+ from django.shortcuts import get_object_or_404
+
+ class BeatFeed(Feed):
+ description_template = 'feeds/beat_description.html'
+
+ def get_object(self, request, beat_id):
+ return get_object_or_404(Beat, pk=beat_id)
+
+ def title(self, obj):
+ return "Chicagocrime.org: Crimes for beat %s" % obj.beat
+
+ def link(self, obj):
+ 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=obj).order_by('-crime_date')[:30]
+
+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.
+
+Also note that :meth:`items()` also follows the same algorithm -- first, it
+tries :meth:`items(obj)`, then :meth:`items()`, then finally an :attr:`items`
+class attribute (which should be a list).
+
+We are using a template for the item descriptions. It can be very simple:
+
+.. code-block:: html+django
+
+ {{ obj.description }}
+
+However, you are free to add formatting as desired.
+
+The ``ExampleFeed`` class below gives full documentation on methods and
+attributes of :class:`~django.contrib.syndication.views.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.views.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 path (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.views.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.views 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.views.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
+
+ urlpatterns = patterns('',
+ # ...
+ (r'^sitenews/rss/$', RssSiteNewsFeed()),
+ (r'^sitenews/atom/$', AtomSiteNewsFeed()),
+ # ...
+ )
+
+Feed class reference
+--------------------
+
+.. class:: django.contrib.syndication.views.Feed
+
+This example illustrates all possible attributes and methods for a
+:class:`~django.contrib.syndication.views.Feed` class::
+
+ from django.contrib.syndication.views 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 a template is not specified, the
+ # item_title() or item_description() methods are used instead.
+
+ 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.
+
+ 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 feed_copyright(self, obj):
+ """
+ Takes the object returned by get_object() and returns the feed's
+ copyright notice as a normal Python string.
+ """
+
+ def feed_copyright(self):
+ """
+ Returns the feed's copyright notice as a normal Python string.
+ """
+
+ feed_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, request, *args, **kwargs):
+ """
+ Takes the current request and the arguments from the URL, and
+ returns an object represented by this feed. Raises
+ django.core.exceptions.ObjectDoesNotExist on error.
+ """
+
+ # ITEM TITLE AND DESCRIPTION -- If title_template or
+ # description_template are not defined, these are used instead. Both are
+ # optional, by default they will use the unicode representation of the
+ # item.
+
+ def item_title(self, item):
+ """
+ Takes an item, as returned by items(), and returns the item's
+ title as a normal Python string.
+ """
+
+ def item_title(self):
+ """
+ Returns the title for every item in the feed.
+ """
+
+ item_title = 'Breaking News: Nothing Happening' # Hard-coded title.
+
+ def item_description(self, item):
+ """
+ Takes an item, as returned by items(), and returns the item's
+ description as a normal Python string.
+ """
+
+ def item_description(self):
+ """
+ Returns the description for every item in the feed.
+ """
+
+ item_description = 'A description of the item.' # Hard-coded description.
+
+ # 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. If not provided, the
+ # item's link is used by default.
+
+ 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
+ >>> from datetime import datetime
+ >>> 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",
+ ... author_name=u"Myself",
+ ... feed_url=u"http://example.com/atom.xml")
+ >>> f.add_item(title=u"Hot dog today",
+ ... link=u"http://www.example.com/entries/1/",
+ ... pubdate=datetime.now(),
+ ... description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
+ >>> print f.writeString('UTF-8')
+ <?xml version="1.0" encoding="UTF-8"?>
+ <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://docs.python.org/library/datetime.html#datetime-objects
+
+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/podcasts/specs.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_attributes(self):
+ attrs = super(iTunesFeed, self).root_attributes()
+ 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
diff --git a/parts/django/docs/ref/contrib/webdesign.txt b/parts/django/docs/ref/contrib/webdesign.txt
new file mode 100644
index 0000000..d355d03
--- /dev/null
+++ b/parts/django/docs/ref/contrib/webdesign.txt
@@ -0,0 +1,56 @@
+========================
+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
+:doc:`"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
+:doc:`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.