diff options
Diffstat (limited to 'parts/django/docs/ref/contrib')
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 Binary files differnew file mode 100644 index 0000000..78a78ae --- /dev/null +++ b/parts/django/docs/ref/contrib/admin/_images/article_actions.png 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 Binary files differnew file mode 100644 index 0000000..6ea9439 --- /dev/null +++ b/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png diff --git a/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png Binary files differnew file mode 100644 index 0000000..391a629 --- /dev/null +++ b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png diff --git a/parts/django/docs/ref/contrib/admin/_images/user_actions.png b/parts/django/docs/ref/contrib/admin/_images/user_actions.png Binary files differnew file mode 100644 index 0000000..fdbe2ad --- /dev/null +++ b/parts/django/docs/ref/contrib/admin/_images/user_actions.png diff --git a/parts/django/docs/ref/contrib/admin/_images/users_changelist.png b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png Binary files differnew file mode 100644 index 0000000..d5f9c01 --- /dev/null +++ b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png 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. |