diff options
Diffstat (limited to 'docs/ref')
28 files changed, 1104 insertions, 188 deletions
diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt index 5e0360c88f..3f82b44f46 100644 --- a/docs/ref/class-based-views/base.txt +++ b/docs/ref/class-based-views/base.txt @@ -8,6 +8,9 @@ themselves or inherited from. They may not provide all the capabilities required for projects, in which case there are Mixins and Generic class-based views. +View +---- + .. class:: django.views.generic.base.View The master class-based base view. All other class-based views inherit from @@ -31,13 +34,13 @@ views. **Example urls.py**:: from django.conf.urls import patterns, url - + from myapp.views import MyView - + urlpatterns = patterns('', url(r'^mine/$', MyView.as_view(), name='my-view'), ) - + **Methods** .. method:: dispatch(request, *args, **kwargs) @@ -61,13 +64,16 @@ views. The default implementation returns ``HttpResponseNotAllowed`` with list of allowed methods in plain text. - - .. note:: + + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods defined on superclasses. +TemplateView +------------ + .. class:: django.views.generic.base.TemplateView Renders a given template, passing it a ``{{ params }}`` template variable, @@ -84,22 +90,22 @@ views. 1. :meth:`dispatch()` 2. :meth:`http_method_not_allowed()` 3. :meth:`get_context_data()` - + **Example views.py**:: from django.views.generic.base import TemplateView - + from articles.models import Article class HomePageView(TemplateView): template_name = "home.html" - + def get_context_data(self, **kwargs): context = super(HomePageView, self).get_context_data(**kwargs) context['latest_articles'] = Article.objects.all()[:5] return context - + **Example urls.py**:: from django.conf.urls import patterns, url @@ -126,12 +132,15 @@ views. * ``params``: The dictionary of keyword arguments captured from the URL pattern that served the view. - .. note:: + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods defined on superclasses. +RedirectView +------------ + .. class:: django.views.generic.base.RedirectView Redirects to a given URL. @@ -159,7 +168,7 @@ views. from django.shortcuts import get_object_or_404 from django.views.generic.base import RedirectView - + from articles.models import Article class ArticleCounterRedirectView(RedirectView): @@ -176,7 +185,7 @@ views. from django.conf.urls import patterns, url from django.views.generic.base import RedirectView - + from article.views import ArticleCounterRedirectView urlpatterns = patterns('', @@ -217,7 +226,7 @@ views. behavior they wish, as long as the method returns a redirect-ready URL string. - .. note:: + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods diff --git a/docs/ref/class-based-views/flattened-index.txt b/docs/ref/class-based-views/flattened-index.txt new file mode 100644 index 0000000000..cbce3690e2 --- /dev/null +++ b/docs/ref/class-based-views/flattened-index.txt @@ -0,0 +1,556 @@ +=========================================== +Class-based generic views - flattened index +=========================================== + +This index provides an alternate organization of the reference documentation +for class-based views. For each view, the effective attributes and methods from +the class tree are represented under that view. For the reference +documentation organized by the class which defines the behavior, see +:doc:`Class-based views</ref/class-based-views/index>` + +Simple generic views +-------------------- + +View +~~~~ +.. class:: View() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.base.View.http_method_names` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` + +TemplateView +~~~~~~~~~~~~ +.. class:: TemplateView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.base.TemplateView.get` +* :meth:`~django.views.generic.base.TemplateView.get_context_data` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +RedirectView +~~~~~~~~~~~~ +.. class:: RedirectView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.base.RedirectView.permanent` +* :attr:`~django.views.generic.base.RedirectView.query_string` +* :attr:`~django.views.generic.base.RedirectView.url` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.RedirectView.delete` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.base.RedirectView.get` +* :meth:`~django.views.generic.base.RedirectView.get_redirect_url` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.base.RedirectView.options` +* :meth:`~django.views.generic.base.RedirectView.post` +* :meth:`~django.views.generic.base.RedirectView.put` + +Detail Views +------------ + +DetailView +~~~~~~~~~~ +.. class:: DetailView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.detail.SingleObjectMixin.model` +* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field` +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.detail.BaseDetailView.get` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +List Views +---------- + +ListView +~~~~~~~~ +.. class:: ListView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.list.BaseListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +Editing views +------------- + +FormView +~~~~~~~~ +.. class:: FormView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.edit.FormMixin.form_invalid` +* :meth:`~django.views.generic.edit.FormMixin.form_valid` +* :meth:`~django.views.generic.edit.ProcessFormView.get` +* :meth:`~django.views.generic.edit.FormMixin.get_context_data` +* :meth:`~django.views.generic.edit.FormMixin.get_form` +* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.edit.ProcessFormView.post` +* :meth:`~django.views.generic.edit.ProcessFormView.put` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +CreateView +~~~~~~~~~~ +.. class:: CreateView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.model` +* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg` +* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field` +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.edit.FormMixin.form_invalid` +* :meth:`~django.views.generic.edit.FormMixin.form_valid` +* :meth:`~django.views.generic.edit.ProcessFormView.get` +* :meth:`~django.views.generic.edit.FormMixin.get_context_data` +* :meth:`~django.views.generic.edit.FormMixin.get_form` +* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.edit.ProcessFormView.post` +* :meth:`~django.views.generic.edit.ProcessFormView.put` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +UpdateView +~~~~~~~~~~ +.. class:: UpdateView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.model` +* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg` +* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field` +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.edit.FormMixin.form_invalid` +* :meth:`~django.views.generic.edit.FormMixin.form_valid` +* :meth:`~django.views.generic.edit.ProcessFormView.get` +* :meth:`~django.views.generic.edit.FormMixin.get_context_data` +* :meth:`~django.views.generic.edit.FormMixin.get_form` +* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.edit.ProcessFormView.post` +* :meth:`~django.views.generic.edit.ProcessFormView.put` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +DeleteView +~~~~~~~~~~ +.. class:: DeleteView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.detail.SingleObjectMixin.model` +* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg` +* :attr:`~django.views.generic.edit.DeletionMixin.success_url` [:meth:`~django.views.generic.edit.DeletionMixin.get_success_url`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field` +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.edit.DeletionMixin.delete` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.detail.BaseDetailView.get` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.edit.DeletionMixin.post` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +Date-based views +---------------- + +ArchiveIndexView +~~~~~~~~~~~~~~~~ +.. class:: ArchiveIndexView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +YearArchiveView +~~~~~~~~~~~~~~~ +.. class:: YearArchiveView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.dates.BaseYearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.BaseYearArchiveView.get_make_object_list`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +MonthArchiveView +~~~~~~~~~~~~~~~~ +.. class:: MonthArchiveView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] +* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.dates.MonthMixin.get_next_month` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +WeekArchiveView +~~~~~~~~~~~~~~~ +.. class:: WeekArchiveView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.WeekMixin.week` [:meth:`~django.views.generic.dates.WeekMixin.get_week`] +* :attr:`~django.views.generic.dates.WeekMixin.week_format` [:meth:`~django.views.generic.dates.WeekMixin.get_week_format`] +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +DayArchiveView +~~~~~~~~~~~~~~ +.. class:: DayArchiveView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`] +* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] +* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.dates.DayMixin.get_next_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_next_month` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.dates.DayMixin.get_previous_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +TodayArchiveView +~~~~~~~~~~~~~~~~ +.. class:: TodayArchiveView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`] +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`] +* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.list.MultipleObjectMixin.model` +* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] +* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] +* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` +* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.dates.BaseDateListView.get` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items` +* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset` +* :meth:`~django.views.generic.dates.DayMixin.get_next_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_next_month` +* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator` +* :meth:`~django.views.generic.dates.DayMixin.get_previous_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + +DateDetailView +~~~~~~~~~~~~~~ +.. class:: DateDetailView() + +**Attributes** (with optional accessor): + +* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] +* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`] +* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`] +* :attr:`~django.views.generic.base.View.http_method_names` +* :attr:`~django.views.generic.detail.SingleObjectMixin.model` +* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] +* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] +* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] +* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg` +* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field` +* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix` +* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`] +* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`] + +**Methods** + +* :meth:`~django.views.generic.base.View.as_view` +* :meth:`~django.views.generic.base.View.dispatch` +* :meth:`~django.views.generic.detail.BaseDetailView.get` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data` +* :meth:`~django.views.generic.dates.DayMixin.get_next_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_next_month` +* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object` +* :meth:`~django.views.generic.dates.DayMixin.get_previous_day` +* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month` +* :meth:`~django.views.generic.base.View.head` +* :meth:`~django.views.generic.base.View.http_method_not_allowed` +* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt index 69a98df77b..12776cbb94 100644 --- a/docs/ref/class-based-views/generic-date-based.txt +++ b/docs/ref/class-based-views/generic-date-based.txt @@ -5,6 +5,9 @@ Generic date views Date-based generic views (in the module :mod:`django.views.generic.dates`) are views for displaying drilldown pages for date-based data. +ArchiveIndexView +---------------- + .. class:: django.views.generic.dates.ArchiveIndexView A top-level index page showing the "latest" objects, by date. Objects with @@ -21,11 +24,17 @@ are views for displaying drilldown pages for date-based data. * :class:`django.views.generic.list.MultipleObjectMixin` * :class:`django.views.generic.dates.DateMixin` * :class:`django.views.generic.base.View` - + **Notes** * Uses a default ``context_object_name`` of ``latest``. * Uses a default ``template_name_suffix`` of ``_archive``. + * Defaults to providing ``date_list`` by year, but this can be altered to + month or day using the attribute ``date_list_period``. This also applies + to all subclass views. + +YearArchiveView +--------------- .. class:: django.views.generic.dates.YearArchiveView @@ -86,6 +95,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_year``. +MonthArchiveView +---------------- + .. class:: django.views.generic.dates.MonthArchiveView A monthly archive page showing all objects in a given month. Objects with a @@ -134,6 +146,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_month``. +WeekArchiveView +--------------- + .. class:: django.views.generic.dates.WeekArchiveView A weekly archive page showing all objects in a given week. Objects with a @@ -175,6 +190,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_week``. +DayArchiveView +-------------- + .. class:: django.views.generic.dates.DayArchiveView A day archive page showing all objects in a given day. Days in the future @@ -225,6 +243,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_day``. +TodayArchiveView +---------------- + .. class:: django.views.generic.dates.TodayArchiveView A day archive page showing all objects for *today*. This is exactly the @@ -246,6 +267,10 @@ are views for displaying drilldown pages for date-based data. * :class:`django.views.generic.dates.DateMixin` * :class:`django.views.generic.base.View` + +DateDetailView +-------------- + .. class:: django.views.generic.dates.DateDetailView A page representing an individual object. If the object has a date value in diff --git a/docs/ref/class-based-views/generic-display.txt b/docs/ref/class-based-views/generic-display.txt index bbf0d4f05a..ef3bc179ee 100644 --- a/docs/ref/class-based-views/generic-display.txt +++ b/docs/ref/class-based-views/generic-display.txt @@ -5,6 +5,9 @@ Generic display views The two following generic class-based views are designed to display data. On many projects they are typically the most commonly used views. +DetailView +---------- + .. class:: django.views.generic.detail.DetailView While this view is executing, ``self.object`` will contain the object that @@ -39,7 +42,7 @@ many projects they are typically the most commonly used views. from articles.models import Article class ArticleDetailView(DetailView): - + model = Article def get_context_data(self, **kwargs): @@ -55,7 +58,10 @@ many projects they are typically the most commonly used views. urlpatterns = patterns('', url(r'^(?P<slug>[-_\w]+)/$', ArticleDetailView.as_view(), name='article-detail'), - ) + ) + +ListView +-------- .. class:: django.views.generic.list.ListView diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt index a65a59bc8b..2fac06ee02 100644 --- a/docs/ref/class-based-views/generic-editing.txt +++ b/docs/ref/class-based-views/generic-editing.txt @@ -2,7 +2,7 @@ Generic editing views ===================== -The following views are described on this page and provide a foundation for +The following views are described on this page and provide a foundation for editing content: * :class:`django.views.generic.edit.FormView` @@ -13,7 +13,7 @@ editing content: .. note:: Some of the examples on this page assume that a model titled 'Author' - has been defined. For these cases we assume the following has been defined + has been defined. For these cases we assume the following has been defined in `myapp/models.py`:: from django import models @@ -25,6 +25,9 @@ editing content: def get_absolute_url(self): return reverse('author-detail', kwargs={'pk': self.pk}) +FormView +-------- + .. class:: django.views.generic.edit.FormView A view that displays a form. On error, redisplays the form with validation @@ -69,6 +72,8 @@ editing content: form.send_email() return super(ContactView, self).form_valid(form) +CreateView +---------- .. class:: django.views.generic.edit.CreateView @@ -94,7 +99,7 @@ editing content: .. attribute:: template_name_suffix The CreateView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_form.html'``. For + ``template_name_suffix`` of ``'_form.html'``. For example, changing this attribute to ``'_create_form.html'`` for a view creating objects for the the example `Author` model would cause the the default `template_name` to be ``'myapp/author_create_form.html'``. @@ -107,6 +112,9 @@ editing content: class AuthorCreate(CreateView): model = Author +UpdateView +---------- + .. class:: django.views.generic.edit.UpdateView A view that displays a form for editing an existing object, redisplaying @@ -133,10 +141,10 @@ editing content: .. attribute:: template_name_suffix The UpdateView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_form.html'``. For + ``template_name_suffix`` of ``'_form.html'``. For example, changing this attribute to ``'_update_form.html'`` for a view updating objects for the the example `Author` model would cause the the - default `template_name` to be ``'myapp/author_update_form.html'``. + default `template_name` to be ``'myapp/author_update_form.html'``. **Example views.py**:: @@ -146,6 +154,9 @@ editing content: class AuthorUpdate(UpdateView): model = Author +DeleteView +---------- + .. class:: django.views.generic.edit.DeleteView A view that displays a confirmation page and deletes an existing object. @@ -171,10 +182,10 @@ editing content: .. attribute:: template_name_suffix The DeleteView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_confirm_delete.html'``. For + ``template_name_suffix`` of ``'_confirm_delete.html'``. For example, changing this attribute to ``'_check_delete.html'`` for a view deleting objects for the the example `Author` model would cause the the - default `template_name` to be ``'myapp/author_check_delete.html'``. + default `template_name` to be ``'myapp/author_check_delete.html'``. **Example views.py**:: @@ -185,4 +196,4 @@ editing content: class AuthorDelete(DeleteView): model = Author - success_url = reverse_lazy('author-list') + success_url = reverse_lazy('author-list') diff --git a/docs/ref/class-based-views/index.txt b/docs/ref/class-based-views/index.txt index f2271d2506..f0e7bbc6c1 100644 --- a/docs/ref/class-based-views/index.txt +++ b/docs/ref/class-based-views/index.txt @@ -6,13 +6,14 @@ Class-based views API reference. For introductory material, see :doc:`/topics/class-based-views/index`. .. toctree:: - :maxdepth: 1 + :maxdepth: 3 base generic-display generic-editing generic-date-based mixins + flattened-index Specification ------------- diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt index a65471e68d..6bf6f10b5d 100644 --- a/docs/ref/class-based-views/mixins-date-based.txt +++ b/docs/ref/class-based-views/mixins-date-based.txt @@ -3,6 +3,9 @@ Date-based mixins ================= +YearMixin +--------- + .. class:: django.views.generic.dates.YearMixin A mixin that can be used to retrieve and provide parsing information for a @@ -36,6 +39,9 @@ Date-based mixins Raises a 404 if no valid year specification can be found. +MonthMixin +---------- + .. class:: django.views.generic.dates.MonthMixin A mixin that can be used to retrieve and provide parsing information for a @@ -82,6 +88,9 @@ Date-based mixins date provided. If ``allow_empty = False``, returns the previous month that contained data. +DayMixin +-------- + .. class:: django.views.generic.dates.DayMixin A mixin that can be used to retrieve and provide parsing information for a @@ -127,6 +136,9 @@ Date-based mixins Returns a date object containing the previous day. If ``allow_empty = False``, returns the previous day that contained data. +WeekMixin +--------- + .. class:: django.views.generic.dates.WeekMixin A mixin that can be used to retrieve and provide parsing information for a @@ -161,6 +173,9 @@ Date-based mixins Raises a 404 if no valid week specification can be found. +DateMixin +--------- + .. class:: django.views.generic.dates.DateMixin A mixin class providing common behavior for all date-based views. @@ -204,6 +219,9 @@ Date-based mixins is greater than the current date/time. Returns :attr:`DateMixin.allow_future` by default. +BaseDateListView +---------------- + .. class:: django.views.generic.dates.BaseDateListView A base class that provides common behavior for all date-based views. There diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt index 89610889db..95dd24f442 100644 --- a/docs/ref/class-based-views/mixins-editing.txt +++ b/docs/ref/class-based-views/mixins-editing.txt @@ -14,6 +14,9 @@ The following mixins are used to construct Django's editing views: Examples of how these are combined into editing views can be found at the documentation on ``Generic editing views``. +FormMixin +--------- + .. class:: django.views.generic.edit.FormMixin A mixin class that provides facilities for creating and displaying forms. @@ -90,6 +93,9 @@ The following mixins are used to construct Django's editing views: :meth:`~django.views.generic.FormMixin.form_invalid`. +ModelFormMixin +-------------- + .. class:: django.views.generic.edit.ModelFormMixin A form mixin that works on ModelForms, rather than a standalone form. @@ -147,12 +153,16 @@ The following mixins are used to construct Django's editing views: .. method:: form_invalid() Renders a response, providing the invalid form as context. - + + +ProcessFormView +--------------- + .. class:: django.views.generic.edit.ProcessFormView A mixin that provides basic HTTP GET and POST workflow. - .. note:: + .. note:: This is named 'ProcessFormView' and inherits directly from :class:`django.views.generic.base.View`, but breaks if used diff --git a/docs/ref/class-based-views/mixins-multiple-object.txt b/docs/ref/class-based-views/mixins-multiple-object.txt index b414355c6b..8bc613b887 100644 --- a/docs/ref/class-based-views/mixins-multiple-object.txt +++ b/docs/ref/class-based-views/mixins-multiple-object.txt @@ -2,6 +2,9 @@ Multiple object mixins ====================== +MultipleObjectMixin +------------------- + .. class:: django.views.generic.list.MultipleObjectMixin A mixin that can be used to display a list of objects. @@ -148,6 +151,9 @@ Multiple object mixins this context variable will be ``None``. +MultipleObjectTemplateResponseMixin +----------------------------------- + .. class:: django.views.generic.list.MultipleObjectTemplateResponseMixin A mixin class that performs template-based response rendering for views diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt index 33d0db3134..61fc945cd3 100644 --- a/docs/ref/class-based-views/mixins-simple.txt +++ b/docs/ref/class-based-views/mixins-simple.txt @@ -2,6 +2,9 @@ Simple mixins ============= +ContextMixin +------------ + .. class:: django.views.generic.base.ContextMixin .. versionadded:: 1.5 @@ -14,8 +17,23 @@ Simple mixins .. method:: get_context_data(**kwargs) - Returns a dictionary representing the template context. The - keyword arguments provided will make up the returned context. + Returns a dictionary representing the template context. The keyword + arguments provided will make up the returned context. + + The template context of all class-based generic views include a + ``view`` variable that points to the ``View`` instance. + + .. admonition:: Use ``alters_data`` where appropriate + + Note that having the view instance in the template context may + expose potentially hazardous methods to template authors. To + prevent methods like this from being called in the template, set + ``alters_data=True`` on those methods. For more information, read + the documentation on :ref:`rendering a template context + <alters-data-description>`. + +TemplateResponseMixin +--------------------- .. class:: django.views.generic.base.TemplateResponseMixin diff --git a/docs/ref/class-based-views/mixins-single-object.txt b/docs/ref/class-based-views/mixins-single-object.txt index a31608dbd4..77f52b96c6 100644 --- a/docs/ref/class-based-views/mixins-single-object.txt +++ b/docs/ref/class-based-views/mixins-single-object.txt @@ -2,6 +2,9 @@ Single object mixins ==================== +SingleObjectMixin +----------------- + .. class:: django.views.generic.detail.SingleObjectMixin Provides a mechanism for looking up an object associated with the @@ -86,6 +89,9 @@ Single object mixins ``context_object_name`` is specified, that variable will also be set in the context, with the same value as ``object``. +SingleObjectTemplateResponseMixin +--------------------------------- + .. class:: django.views.generic.detail.SingleObjectTemplateResponseMixin A mixin class that performs template-based response rendering for views diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 4d39981a4d..66a5a2cc4f 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -561,8 +561,6 @@ subclass:: .. attribute:: ModelAdmin.list_filter - .. versionchanged:: 1.4 - Set ``list_filter`` to activate filters in the right sidebar of the change list page of the admin, as illustrated in the following screenshot: @@ -586,6 +584,8 @@ subclass:: class PersonAdmin(UserAdmin): list_filter = ('company__name',) + .. versionadded:: 1.4 + * a class inheriting from :mod:`django.contrib.admin.SimpleListFilter`, which you need to provide the ``title`` and ``parameter_name`` attributes to and override the ``lookups`` and ``queryset`` methods, @@ -623,7 +623,7 @@ subclass:: provided in the query string and retrievable via `self.value()`. """ - # Compare the requested value (either '80s' or 'other') + # Compare the requested value (either '80s' or '90s') # to decide how to filter the queryset. if self.value() == '80s': return queryset.filter(birthday__gte=date(1980, 1, 1), @@ -671,6 +671,8 @@ subclass:: birthday__lte=date(1999, 12, 31)).exists(): yield ('90s', _('in the nineties')) + .. versionadded:: 1.4 + * a tuple, where the first element is a field name and the second element is a class inheriting from :mod:`django.contrib.admin.FieldListFilter`, for example:: diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt index af937e036e..4b1dd96280 100644 --- a/docs/ref/contrib/comments/index.txt +++ b/docs/ref/contrib/comments/index.txt @@ -158,11 +158,13 @@ For example:: .. warning:: - There's a known bug in Safari/Webkit which causes the named anchor to be + 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. +.. _`known bug`: https://bugs.webkit.org/show_bug.cgi?id=24175 + .. templatetag:: get_comment_count Counting comments diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index f25cb31e4b..8d352ff8b2 100644 --- a/docs/ref/contrib/csrf.txt +++ b/docs/ref/contrib/csrf.txt @@ -84,47 +84,94 @@ AJAX While the above method can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each -XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF +XMLHttpRequest, set a custom ``X-CSRFToken`` header to the value of the CSRF token. This is often easier, because many javascript frameworks provide hooks -that allow headers to be set on every request. In jQuery, you can use the -``ajaxSend`` event as follows: +that allow headers to be set on every request. + +As a first step, you must get the CSRF token itself. The recommended source for +the token is the ``csrftoken`` cookie, which will be set if you've enabled CSRF +protection for your views as outlined above. + +.. note:: + + The CSRF token cookie is named ``csrftoken`` by default, but you can control + the cookie name via the :setting:`CSRF_COOKIE_NAME` setting. + +Acquiring the token is straightforward: .. code-block:: javascript - jQuery(document).ajaxSend(function(event, xhr, settings) { - function getCookie(name) { - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } + // using jQuery + function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; } } - return cookieValue; - } - function sameOrigin(url) { - // url could be relative or scheme relative or absolute - var host = document.location.host; // host + port - var protocol = document.location.protocol; - var sr_origin = '//' + host; - var origin = protocol + sr_origin; - // Allow absolute or scheme relative URLs to same origin - return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || - (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || - // or any other URL that isn't scheme relative or absolute i.e relative. - !(/^(\/\/|http:|https:).*/.test(url)); - } - function safeMethod(method) { - return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } + return cookieValue; + } + var csrftoken = getCookie('csrftoken'); + +The above code could be simplified by using the `jQuery cookie plugin +<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``: + +.. code-block:: javascript + + var csrftoken = $.cookie('csrftoken'); + +.. note:: + + The CSRF token is also present in the DOM, but only if explicitly included + using :ttag:`csrf_token` in a template. The cookie contains the canonical + token; the ``CsrfViewMiddleware`` will prefer the cookie to the token in + the DOM. Regardless, you're guaranteed to have the cookie if the token is + present in the DOM, so you should use the cookie! - if (!safeMethod(settings.type) && sameOrigin(settings.url)) { - xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); +.. warning:: + + If your view is not rendering a template containing the :ttag:`csrf_token` + template tag, Django might not set the CSRF token cookie. This is common in + cases where forms are dynamically added to the page. To address this case, + Django provides a view decorator which forces setting of the cookie: + :func:`~django.views.decorators.csrf.ensure_csrf_cookie`. + +Finally, you'll have to actually set the header on your AJAX request, while +protecting the CSRF token from being sent to other domains. + +.. code-block:: javascript + + function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + } + function sameOrigin(url) { + // test that a given url is a same-origin URL + // url could be relative or scheme relative or absolute + var host = document.location.host; // host + port + var protocol = document.location.protocol; + var sr_origin = '//' + host; + var origin = protocol + sr_origin; + // Allow absolute or scheme relative URLs to same origin + return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || + (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || + // or any other URL that isn't scheme relative or absolute i.e relative. + !(/^(\/\/|http:|https:).*/.test(url)); + } + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { + // Send the token to same-origin, relative URLs only. + // Send the token only if the method warrants CSRF protection + // Using the CSRFToken value acquired earlier + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } } }); @@ -133,18 +180,32 @@ that allow headers to be set on every request. In jQuery, you can use the Due to a bug introduced in jQuery 1.5, the example above will not work correctly on that version. Make sure you are running at least jQuery 1.5.1. -Adding this to a javascript file that is included on your site will ensure that -AJAX POST requests that are made via jQuery will not be caught by the CSRF -protection. +You can use `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in +jQuery 1.5 and newer in order to replace the `sameOrigin` logic above: -The above code could be simplified by using the `jQuery cookie plugin -<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``, and -`settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and -later to replace ``sameOrigin``. +.. code-block:: javascript + + function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + } + $.ajaxSetup({ + crossDomain: false, // obviates need for sameOrigin test + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type)) { + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } + }); + +.. note:: + + In a `security release blogpost`_, a simpler "same origin test" example + was provided which only checked for a relative URL. The ``sameOrigin`` + test above supersedes that example—it works for edge cases like + scheme-relative or absolute URLs for the same domain. -In addition, if the CSRF cookie has not been sent to the client by use of -:ttag:`csrf_token`, you may need to ensure the client receives the cookie by -using :func:`~django.views.decorators.csrf.ensure_csrf_cookie`. +.. _security release blogpost: https://www.djangoproject.com/weblog/2011/feb/08/security/ Other template engines ---------------------- diff --git a/docs/ref/contrib/flatpages.txt b/docs/ref/contrib/flatpages.txt index 6b5773b5a4..3de449708f 100644 --- a/docs/ref/contrib/flatpages.txt +++ b/docs/ref/contrib/flatpages.txt @@ -42,6 +42,16 @@ To install the flatpages app, follow these steps: 2. Add ``'django.contrib.flatpages'`` to your :setting:`INSTALLED_APPS` setting. +Then either: + +3. Add an entry in your URLconf. For example:: + + urlpatterns = patterns('', + ('^pages/', include('django.contrib.flatpages.urls')), + ) + +or: + 3. Add ``'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'`` to your :setting:`MIDDLEWARE_CLASSES` setting. @@ -57,8 +67,38 @@ 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. +Using the URLconf +----------------- + +There are several ways to include the flat pages in your URLconf. You can +dedicate a particular path to flat pages:: + + urlpatterns = patterns('', + ('^pages/', include('django.contrib.flatpages.urls')), + ) + +You can also set it up as a "catchall" pattern. In this case, it is important +to place the pattern at the end of the other urlpatterns:: + + # Your other patterns here + urlpatterns += patterns('django.contrib.flatpages.views', + (r'^(?P<url>.*)$', 'flatpage'), + ) + +Another common setup is to use flat pages for a limited set of known pages and +to hard code the urls, so you can reference them with the :ttag:`url` template +tag:: + + urlpatterns += patterns('django.contrib.flatpages.views', + url(r'^about-us/$', 'flatpage', {'url': '/about-us/'}, name='about'), + url(r'^license/$', 'flatpage', {'url': '/license/'}, name='license'), + ) + +Using the middleware +-------------------- + The :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` -does all of the work. +can do all of the work. .. class:: FlatpageFallbackMiddleware @@ -255,4 +295,3 @@ For example: {% get_flatpages '/about/' as about_pages %} {% get_flatpages about_prefix as about_pages %} {% get_flatpages '/about/' for someuser as about_pages %} - diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 7d229a5d66..b8e585a4d2 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -86,8 +86,8 @@ the message itself. Here's what the :file:`forms.py` might look like:: section :ref:`Handling files <wizard-files>` below to learn more about what to do. -Creating a ``WizardView`` class -------------------------------- +Creating a ``WizardView`` subclass +---------------------------------- The next step is to create a :class:`django.contrib.formtools.wizard.views.WizardView` subclass. You can @@ -528,7 +528,7 @@ We define our wizard in a ``views.py``:: We need to add the ``ContactWizard`` to our ``urls.py`` file:: - from django.conf.urls import pattern + from django.conf.urls import patterns from myapp.forms import ContactForm1, ContactForm2 from myapp.views import ContactWizard, show_message_form_condition diff --git a/docs/ref/contrib/gis/install.txt b/docs/ref/contrib/gis/install.txt index 72bd72a6f8..3e952c173b 100644 --- a/docs/ref/contrib/gis/install.txt +++ b/docs/ref/contrib/gis/install.txt @@ -60,14 +60,14 @@ 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. -================== ============================== ================== ========================================================== +SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 2.3+, pysqlite2 2.5+ +================== ============================== ================== ========================================= .. _geospatial_libs: @@ -140,7 +140,7 @@ 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 +First, download GEOS 3.3.0 from the refractions Web site and untar the source archive:: $ wget http://download.osgeo.org/geos/geos-3.3.0.tar.bz2 @@ -421,7 +421,7 @@ 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/gaia-sins/libspatialite-sources/libspatialite-amalgamation-2.3.1.tar.gz - $ wget http://www.gaia-gis.it/gaia-sins/libspatialite-sources/spatialite-tools-2.3.1.tar.gz + $ wget http://www.gaia-gis.it/gaia-sins/spatialite-tools-sources/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 @@ -467,8 +467,8 @@ 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:: +``enable_load_extension`` method, which is only available in versions 2.5+ of +pysqlite2. 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 @@ -582,8 +582,8 @@ Creating a spatial database for SpatiaLite After you've installed SpatiaLite, you'll need to create a number of spatial metadata tables in your database in order to perform spatial queries. -If you're using SpatiaLite 3.0 or newer, use the ``spatialite`` utility to -call the ``InitSpatiaMetaData()`` function, like this:: +If you're using SpatiaLite 2.4 or newer, use the ``spatialite`` utility to +call the ``InitSpatialMetaData()`` function, like this:: $ spatialite geodjango.db "SELECT InitSpatialMetaData();" the SPATIAL_REF_SYS table already contains some row(s) @@ -593,19 +593,17 @@ call the ``InitSpatiaMetaData()`` function, like this:: You can safely ignore the error messages shown. When you've done this, you can skip the rest of this section. -If you're using a version of SpatiaLite older than 3.0, you'll need to download -a database-initialization file and execute its SQL queries in your database. +If you're using SpatiaLite 2.3, you'll need to download a +database-initialization file and execute its SQL queries in your database. -First, get it from the appropriate SpatiaLite Resources page ( -http://www.gaia-gis.it/spatialite-2.3.1/resources.html for 2.3 or -http://www.gaia-gis.it/spatialite-2.4.0/ for 2.4):: +First, get it from the `SpatiaLite Resources`__ page:: $ wget http://www.gaia-gis.it/spatialite-2.3.1/init_spatialite-2.3.sql.gz $ gunzip init_spatialite-2.3.sql.gz Then, use the ``spatialite`` command to initialize a spatial database:: - $ spatialite geodjango.db < init_spatialite-2.X.sql + $ spatialite geodjango.db < init_spatialite-2.3.sql .. note:: @@ -613,6 +611,8 @@ Then, use the ``spatialite`` command to initialize a spatial database:: you want to use. Use the same in the :setting:`DATABASES` ``"name"`` key inside your ``settings.py``. +__ http://www.gaia-gis.it/spatialite-2.3.1/resources.html + Add ``django.contrib.gis`` to :setting:`INSTALLED_APPS` ------------------------------------------------------- @@ -643,10 +643,6 @@ Invoke the Django shell from your project and execute the >>> 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. @@ -824,8 +820,10 @@ Download the framework packages for: * 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`__. +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:: diff --git a/docs/ref/contrib/gis/testing.txt b/docs/ref/contrib/gis/testing.txt index 78663b967c..d12c884a1b 100644 --- a/docs/ref/contrib/gis/testing.txt +++ b/docs/ref/contrib/gis/testing.txt @@ -119,9 +119,10 @@ Settings ``SPATIALITE_SQL`` ^^^^^^^^^^^^^^^^^^ -Only relevant when using a SpatiaLite version older than 3.0. +Only relevant when using a SpatiaLite version 2.3. -By default, the GeoDjango test runner looks for the SpatiaLite SQL in the +By default, the GeoDjango test runner looks for the :ref:`file containing the +SpatiaLite dababase-initialization SQL code <create_spatialite_db>` in the same directory where it was invoked (by default the same directory where ``manage.py`` is located). To use a different location, add the following to your settings:: diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt index 3a63493137..15863aee7b 100644 --- a/docs/ref/contrib/gis/tutorial.txt +++ b/docs/ref/contrib/gis/tutorial.txt @@ -671,6 +671,17 @@ of abstraction:: __ http://spatialreference.org/ref/epsg/32140/ +.. admonition:: Raw queries + + When using :doc:`raw queries </topics/db/sql>`, you should generally wrap + your geometry fields with the ``asText()`` SQL function so as the field + value will be recognized by GEOS:: + + City.objects.raw('SELECT id, name, asText(point) from myapp_city') + + This is not absolutely required by PostGIS, but generally you should only + use raw queries when you know exactly what you are doing. + Lazy Geometries --------------- Geometries come to GeoDjango in a standardized textual representation. Upon diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 92b5665bea..3e256e9d9e 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -56,10 +56,10 @@ will do some additional queries to set these parameters. Transaction handling --------------------- -:doc:`By default </topics/db/transactions>`, Django starts a transaction when a -database connection is first used and commits the result at the end of the -request/response handling. The PostgreSQL backends normally operate the same -as any other Django backend in this respect. +:doc:`By default </topics/db/transactions>`, Django runs with an open +transaction which it commits automatically when any built-in, data-altering +model function is called. The PostgreSQL backends normally operate the same as +any other Django backend in this respect. .. _postgresql-autocommit-mode: diff --git a/docs/ref/files/file.txt b/docs/ref/files/file.txt index 99547f1c9e..ada614df45 100644 --- a/docs/ref/files/file.txt +++ b/docs/ref/files/file.txt @@ -91,14 +91,18 @@ The ``ContentFile`` Class .. class:: ContentFile(File) The ``ContentFile`` class inherits from :class:`~django.core.files.File`, - but unlike :class:`~django.core.files.File` it operates on string content, - rather than an actual file. For example:: + but unlike :class:`~django.core.files.File` it operates on string content + (bytes also supported), rather than an actual file. For example:: from __future__ import unicode_literals from django.core.files.base import ContentFile - f1 = ContentFile(b"my string content") - f2 = ContentFile("my unicode content encoded as UTF-8".encode('UTF-8')) + f1 = ContentFile("esta sentencia está en español") + f2 = ContentFile(b"these are bytes") + + .. versionchanged:: 1.5 + + ContentFile also accepts Unicode strings. .. currentmodule:: django.core.files.images diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index a43163c5e9..275c696230 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -70,8 +70,8 @@ If ``True``, the field is allowed to be blank. Default is ``False``. Note that this is different than :attr:`~Field.null`. :attr:`~Field.null` is purely database-related, whereas :attr:`~Field.blank` is validation-related. If -a field has ``blank=True``, validation on Django's admin site will allow entry -of an empty value. If a field has ``blank=False``, the field will be required. +a field has ``blank=True``, form validation will allow entry of an empty value. +If a field has ``blank=False``, the field will be required. .. _field-choices: @@ -81,14 +81,11 @@ of an empty value. If a field has ``blank=False``, the field will be required. .. attribute:: Field.choices An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this -field. +field. If this is given, the default form widget will be a select box with +these choices instead of the standard text field. -If this is given, Django's admin will use a select box instead of the standard -text field and will limit choices to the choices given. - -A choices list is an iterable of 2-tuples; the first element in each -tuple is the actual value to be stored, and the second element is the -human-readable name. For example:: +The first element in each tuple is the actual value to be stored, and the +second element is the human-readable name. For example:: YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), @@ -176,7 +173,7 @@ scenes. .. attribute:: Field.db_index -If ``True``, djadmin:`django-admin.py sqlindexes <sqlindexes>` will output a +If ``True``, :djadmin:`django-admin.py sqlindexes <sqlindexes>` will output a ``CREATE INDEX`` statement for this field. ``db_tablespace`` @@ -203,8 +200,8 @@ callable it will be called every time a new object is created. .. attribute:: Field.editable -If ``False``, the field will not be editable in the admin or via forms -automatically generated from the model class. Default is ``True``. +If ``False``, the field will not be displayed in the admin or any other +:class:`~django.forms.ModelForm`. Default is ``True``. ``error_messages`` ------------------ @@ -224,11 +221,11 @@ the `Field types`_ section below. .. attribute:: Field.help_text -Extra "help" text to be displayed under the field on the object's admin form. -It's useful for documentation even if your object doesn't have an admin form. +Extra "help" text to be displayed with the form widget. It's useful for +documentation even if your field isn't used on a form. -Note that this value is *not* HTML-escaped when it's displayed in the admin -interface. This lets you include HTML in :attr:`~Field.help_text` if you so +Note that this value is *not* HTML-escaped in automatically-generated +forms. This lets you include HTML in :attr:`~Field.help_text` if you so desire. For example:: help_text="Please use the following format: <em>YYYY-MM-DD</em>." @@ -259,7 +256,7 @@ Only one primary key is allowed on an object. If ``True``, this field must be unique throughout the table. -This is enforced at the database level and at the Django admin-form level. If +This is enforced at the database level and by model validation. If you try to save a model with a duplicate value in a :attr:`~Field.unique` field, a :exc:`django.db.IntegrityError` will be raised by the model's :meth:`~django.db.models.Model.save` method. @@ -279,7 +276,7 @@ For example, if you have a field ``title`` that has ``unique_for_date="pub_date"``, then Django wouldn't allow the entry of two records with the same ``title`` and ``pub_date``. -This is enforced at the Django admin-form level but not at the database level. +This is enforced by model validation but not at the database level. ``unique_for_month`` -------------------- @@ -337,7 +334,7 @@ otherwise. See :ref:`automatic-primary-key-fields`. A 64 bit integer, much like an :class:`IntegerField` except that it is guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The -admin represents this as an ``<input type="text">`` (a single-line input). +default form widget for this field is a :class:`~django.forms.TextInput`. ``BooleanField`` @@ -347,7 +344,8 @@ admin represents this as an ``<input type="text">`` (a single-line input). A true/false field. -The admin represents this as a checkbox. +The default form widget for this field is a +:class:`~django.forms.CheckboxInput`. If you need to accept :attr:`~Field.null` values then use :class:`NullBooleanField` instead. @@ -361,7 +359,7 @@ A string field, for small- to large-sized strings. For large amounts of text, use :class:`~django.db.models.TextField`. -The admin represents this as an ``<input type="text">`` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. :class:`CharField` has one extra required argument: @@ -414,9 +412,10 @@ optional arguments: for creation of timestamps. Note that the current date is *always* used; it's not just a default value that you can override. -The admin represents this as an ``<input type="text">`` with a JavaScript -calendar, and a shortcut for "Today". Includes an additional ``invalid_date`` -error message key. +The default form widget for this field is a +:class:`~django.forms.TextInput`. The admin adds a JavaScript calendar, +and a shortcut for "Today". Includes an additional ``invalid_date`` error +message key. .. note:: As currently implemented, setting ``auto_now`` or ``auto_now_add`` to @@ -431,8 +430,9 @@ error message key. A date and time, represented in Python by a ``datetime.datetime`` instance. Takes the same extra arguments as :class:`DateField`. -The admin represents this as two ``<input type="text">`` fields, with -JavaScript shortcuts. +The default form widget for this field is a single +:class:`~django.forms.TextInput`. The admin uses two separate +:class:`~django.forms.TextInput` widgets with JavaScript shortcuts. ``DecimalField`` ---------------- @@ -461,7 +461,7 @@ decimal places:: models.DecimalField(..., max_digits=19, decimal_places=10) -The admin represents this as an ``<input type="text">`` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. .. note:: @@ -539,8 +539,8 @@ Also has one optional argument: Optional. A storage object, which handles the storage and retrieval of your files. See :doc:`/topics/files` for details on how to provide this object. -The admin represents this field as an ``<input type="file">`` (a file-upload -widget). +The default form widget for this field is a +:class:`~django.forms.widgets.FileInput`. Using a :class:`FileField` or an :class:`ImageField` (see below) in a model takes a few steps: @@ -725,7 +725,7 @@ can change the maximum length using the :attr:`~CharField.max_length` argument. A floating-point number represented in Python by a ``float`` instance. -The admin represents this as an ``<input type="text">`` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. .. _floatfield_vs_decimalfield: @@ -776,16 +776,16 @@ length using the :attr:`~CharField.max_length` argument. .. class:: IntegerField([**options]) -An integer. The admin represents this as an ``<input type="text">`` (a -single-line input). +An integer. The default form widget for this field is a +:class:`~django.forms.TextInput`. ``IPAddressField`` ------------------ .. class:: IPAddressField([**options]) -An IP address, in string format (e.g. "192.0.2.30"). The admin represents this -as an ``<input type="text">`` (a single-line input). +An IP address, in string format (e.g. "192.0.2.30"). The default form widget +for this field is a :class:`~django.forms.TextInput`. ``GenericIPAddressField`` ------------------------- @@ -795,8 +795,8 @@ as an ``<input type="text">`` (a single-line input). .. versionadded:: 1.4 An IPv4 or IPv6 address, in string format (e.g. ``192.0.2.30`` or -``2a02:42fe::4``). The admin represents this as an ``<input type="text">`` -(a single-line input). +``2a02:42fe::4``). The default form widget for this field is a +:class:`~django.forms.TextInput`. The IPv6 address normalization follows :rfc:`4291#section-2.2` section 2.2, including using the IPv4 format suggested in paragraph 3 of that section, like @@ -823,8 +823,8 @@ are converted to lowercase. .. class:: NullBooleanField([**options]) Like a :class:`BooleanField`, but allows ``NULL`` as one of the options. Use -this instead of a :class:`BooleanField` with ``null=True``. The admin represents -this as a ``<select>`` box with "Unknown", "Yes" and "No" choices. +this instead of a :class:`BooleanField` with ``null=True``. The default form +widget for this field is a :class:`~django.forms.NullBooleanSelect`. ``PositiveIntegerField`` ------------------------ @@ -875,8 +875,8 @@ Like an :class:`IntegerField`, but only allows values under a certain .. class:: TextField([**options]) -A large text field. The admin represents this as a ``<textarea>`` (a multi-line -input). +A large text field. The default form widget for this field is a +:class:`~django.forms.Textarea`. .. admonition:: MySQL users @@ -893,8 +893,8 @@ input). A time, represented in Python by a ``datetime.time`` instance. Accepts the same auto-population options as :class:`DateField`. -The admin represents this as an ``<input type="text">`` with some JavaScript -shortcuts. +The default form widget for this field is a :class:`~django.forms.TextInput`. +The admin adds some JavaScript shortcuts. ``URLField`` ------------ @@ -903,7 +903,7 @@ shortcuts. A :class:`CharField` for a URL. -The admin represents this as an ``<input type="text">`` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. Like all :class:`CharField` subclasses, :class:`URLField` takes the optional :attr:`~CharField.max_length`argument. If you don't specify @@ -979,9 +979,9 @@ define the details of how the relation works. .. attribute:: ForeignKey.limit_choices_to A dictionary of lookup arguments and values (see :doc:`/topics/db/queries`) - that limit the available admin choices for this object. Use this with - functions from the Python ``datetime`` module to limit choices of objects by - date. For example:: + that limit the available admin or ModelForm choices for this object. Use + this with functions from the Python ``datetime`` module to limit choices of + objects by date. For example:: limit_choices_to = {'pub_date__lte': datetime.now} diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 14541ad0d1..472ac96457 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -48,7 +48,7 @@ that, you need to :meth:`~Model.save()`. 2. Add a method on a custom manager (usually preferred):: class BookManager(models.Manager): - def create_book(title): + def create_book(self, title): book = self.create(title=title) # do something with the book return book @@ -386,6 +386,12 @@ perform an update on all fields. Specifying ``update_fields`` will force an update. +When saving a model fetched through deferred model loading +(:meth:`~Model.only()` or :meth:`~Model.defer()`) only the fields loaded from +the DB will get updated. In effect there is an automatic ``update_fields`` in +this case. If you assign or change any deferred field value, these fields will +be added to the updated fields. + Deleting objects ================ @@ -453,9 +459,9 @@ using ``__str__()`` like this:: last_name = models.CharField(max_length=50) def __str__(self): - # Note use of django.utils.encoding.smart_bytes() here because + # Note use of django.utils.encoding.force_bytes() here because # first_name and last_name will be unicode strings. - return smart_bytes('%s %s' % (self.first_name, self.last_name)) + return force_bytes('%s %s' % (self.first_name, self.last_name)) ``get_absolute_url`` -------------------- diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 8c188c67c3..b59b2ece82 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1110,6 +1110,14 @@ one, doing so will result in an error. reader, is slightly faster and consumes a little less memory in the Python process. +.. versionchanged:: 1.5 + +.. note:: + + When calling :meth:`~Model.save()` for instances with deferred fields, + only the loaded fields will be saved. See :meth:`~Model.save()` for more + details. + only ~~~~ @@ -1154,6 +1162,14 @@ All of the cautions in the note for the :meth:`defer` documentation apply to options. Also note that using :meth:`only` and omitting a field requested using :meth:`select_related` is an error as well. +.. versionchanged:: 1.5 + +.. note:: + + When calling :meth:`~Model.save()` for instances with deferred fields, + only the loaded fields will be saved. See :meth:`~Model.save()` for more + details. + using ~~~~~ diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 551ee00c6b..21e99de10d 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -731,10 +731,11 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in .. class:: HttpResponseRedirect - The constructor takes a single argument -- the path to redirect to. This - can be a fully qualified URL (e.g. ``'http://www.yahoo.com/search/'``) or - an absolute path with no domain (e.g. ``'/search/'``). Note that this - returns an HTTP status code 302. + The first argument to the constructor is required -- the path to redirect + to. This can be a fully qualified URL + (e.g. ``'http://www.yahoo.com/search/'``) or an absolute path with no + domain (e.g. ``'/search/'``). See :class:`HttpResponse` for other optional + constructor arguments. Note that this returns an HTTP status code 302. .. class:: HttpResponsePermanentRedirect @@ -743,8 +744,9 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in .. class:: HttpResponseNotModified - The constructor doesn't take any arguments. Use this to designate that a - page hasn't been modified since the user's last request (status code 304). + The constructor doesn't take any arguments and no content should be added + to this response. Use this to designate that a page hasn't been modified + since the user's last request (status code 304). .. class:: HttpResponseBadRequest @@ -760,8 +762,9 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in .. class:: HttpResponseNotAllowed - Like :class:`HttpResponse`, but uses a 405 status code. Takes a single, - required argument: a list of permitted methods (e.g. ``['GET', 'POST']``). + Like :class:`HttpResponse`, but uses a 405 status code. The first argument + to the constructor is required: a list of permitted methods (e.g. + ``['GET', 'POST']``). .. class:: HttpResponseGone diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 531ff33da2..4729a2b6f1 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -183,7 +183,7 @@ compose a prefix, version and key into a final cache key. The default implementation is equivalent to the function:: def make_key(key, key_prefix, version): - return ':'.join([key_prefix, str(version), smart_bytes(key)]) + return ':'.join([key_prefix, str(version), key]) You may use any key function you want, as long as it has the same argument signature. @@ -747,14 +747,15 @@ Did you catch that? NEVER deploy a site into production with :setting:`DEBUG` turned on. One of the main features of debug mode is the display of detailed error pages. -If your app raises an exception when ``DEBUG`` is ``True``, Django will display -a detailed traceback, including a lot of metadata about your environment, such -as all the currently defined Django settings (from ``settings.py``). +If your app raises an exception when :setting:`DEBUG` is ``True``, Django will +display a detailed traceback, including a lot of metadata about your +environment, such as all the currently defined Django settings (from +``settings.py``). As a security measure, Django will *not* include settings that might be -sensitive (or offensive), such as ``SECRET_KEY`` or ``PROFANITIES_LIST``. -Specifically, it will exclude any setting whose name includes any of the -following: +sensitive (or offensive), such as :setting:`SECRET_KEY` or +:setting:`PROFANITIES_LIST`. Specifically, it will exclude any setting whose +name includes any of the following: * API * KEY @@ -1356,7 +1357,7 @@ MANAGERS Default: ``()`` (Empty tuple) A tuple in the same format as :setting:`ADMINS` that specifies who should get -broken-link notifications when ``SEND_BROKEN_LINK_EMAILS=True``. +broken-link notifications when :setting:`SEND_BROKEN_LINK_EMAILS` is ``True``. .. setting:: MEDIA_ROOT @@ -1365,8 +1366,8 @@ MEDIA_ROOT Default: ``''`` (Empty string) -Absolute path to the directory that holds media for this installation, used -for :doc:`managing stored files </topics/files>`. +Absolute filesystem path to the directory that will hold :doc:`user-uploaded +files </topics/files>`. Example: ``"/var/www/example.com/media/"`` @@ -1537,9 +1538,23 @@ SECRET_KEY Default: ``''`` (Empty string) -A secret key for this particular Django installation. Used to provide a seed in -secret-key hashing algorithms. Set this to a random string -- the longer, the -better. ``django-admin.py startproject`` creates one automatically. +A secret key for a particular Django installation. This is used to provide +:doc:`cryptographic signing </topics/signing>`, and should be set to a unique, +unpredictable value. + +:djadmin:`django-admin.py startproject <startproject>` automatically adds a +randomly-generated ``SECRET_KEY`` to each new project. + +.. warning:: + + **Keep this value secret.** + + Running Django with a known :setting:`SECRET_KEY` defeats many of Django's + security protections, and can lead to privilege escalation and remote code + execution vulnerabilities. + +.. versionchanged:: 1.5 + Django will now refuse to start if :setting:`SECRET_KEY` is not set. .. setting:: SECURE_PROXY_SSL_HEADER diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index bd2b4c6e9d..48bd346788 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -125,6 +125,10 @@ dot in a variable name, it tries the following lookups, in this order: * Attribute lookup. Example: ``foo.bar`` * List-index lookup. Example: ``foo[bar]`` +Note that "bar" in a template expression like ``{{ foo.bar }}`` will be +interpreted as a literal string and not using the value of the variable "bar", +if one exists in the template context. + The template system uses the first lookup type that works. It's short-circuit logic. Here are a few examples:: @@ -198,6 +202,8 @@ straight lookups. Here are some things to keep in mind: * A variable can only be called if it has no required arguments. Otherwise, the system will return an empty string. +.. _alters-data-description: + * Obviously, there can be side effects when calling some variables, and it'd be either foolish or a security hole to allow the template system to access them. @@ -422,6 +428,10 @@ optional, third positional argument, ``processors``. In this example, the my_data_dictionary, context_instance=RequestContext(request)) + Alternatively, use the :meth:`~django.shortcuts.render()` shortcut which is + the same as a call to :func:`~django.shortcuts.render_to_response()` with a + context_instance argument that forces the use of a ``RequestContext``. + Here's what each of the default processors does: django.contrib.auth.context_processors.auth diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index b6cb1035d3..de19578cac 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -187,6 +187,14 @@ The functions defined in this module share the following properties: Useful as a mix-in. If you support Python 2 and 3 with a single code base, you can inherit this mix-in and just define ``__unicode__``. +.. function:: python_2_unicode_compatible + + A decorator that defines ``__unicode__`` and ``__str__`` methods under + Python 2. Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a ``__str__`` + method returning text and apply this decorator to the class. + .. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict') .. versionadded:: 1.5 @@ -232,14 +240,29 @@ The functions defined in this module share the following properties: If ``strings_only`` is ``True``, don't convert (some) non-string-like objects. +.. function:: force_bytes(s, encoding='utf-8', strings_only=False, errors='strict') + + .. versionadded:: 1.5 + + Similar to ``smart_bytes``, except that lazy instances are resolved to + bytestrings, rather than kept as lazy objects. + + If ``strings_only`` is ``True``, don't convert (some) non-string-like + objects. + .. function:: smart_str(s, encoding='utf-8', strings_only=False, errors='strict') Alias of :func:`smart_bytes` on Python 2 and :func:`smart_text` on Python - 3. This function always returns a :class:`str`. + 3. This function returns a :class:`str` or a lazy string. For instance, this is suitable for writing to :attr:`sys.stdout` on Python 2 and 3. +.. function:: force_str(s, encoding='utf-8', strings_only=False, errors='strict') + + Alias of :func:`force_bytes` on Python 2 and :func:`force_text` on Python + 3. This function always returns a :class:`str`. + .. function:: iri_to_uri(iri) Convert an Internationalized Resource Identifier (IRI) portion to a URI @@ -478,6 +501,33 @@ escaping HTML. through :func:`conditional_escape` which (ultimately) calls :func:`~django.utils.encoding.force_text` on the values. +.. function:: strip_tags(value) + + Removes anything that looks like an html tag from the string, that is + anything contained within ``<>``. + + For example:: + + strip_tags(value) + + If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the + return value will be ``"Joel is a slug"``. + +.. function:: remove_tags(value, tags) + + Removes a list of [X]HTML tag names from the output. + + For example:: + + remove_tags(value, ["b", "span"]) + + If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the + return value will be ``"Joel <button>is</button> a slug"``. + + Note that this filter is case-sensitive. + + If ``value`` is ``"<B>Joel</B> <button>is</button> a <span>slug</span>"`` the + return value will be ``"<B>Joel</B> <button>is</button> a slug"``. .. _str.format: http://docs.python.org/library/stdtypes.html#str.format @@ -552,15 +602,29 @@ string" means that the producer of the string has already turned characters that should not be interpreted by the HTML engine (e.g. '<') into the appropriate entities. +.. class:: SafeBytes + + .. versionadded:: 1.5 + + A :class:`bytes` subclass that has been specifically marked as "safe" + (requires no further escaping) for HTML output purposes. + .. class:: SafeString - A string subclass that has been specifically marked as "safe" (requires no - further escaping) for HTML output purposes. + A :class:`str` subclass that has been specifically marked as "safe" + (requires no further escaping) for HTML output purposes. This is + :class:`SafeBytes` on Python 2 and :class:`SafeText` on Python 3. + +.. class:: SafeText + + .. versionadded:: 1.5 + + A :class:`str` (in Python 3) or :class:`unicode` (in Python 2) subclass + that has been specifically marked as "safe" for HTML output purposes. .. class:: SafeUnicode - A unicode subclass that has been specifically marked as "safe" for HTML - output purposes. + Historical name of :class:`SafeText`. Only available under Python 2. .. function:: mark_safe(s) @@ -577,6 +641,24 @@ appropriate entities. Can be called multiple times on a single string (the resulting escaping is only applied once). +``django.utils.text`` +===================== + +.. module:: django.utils.text + :synopsis: Text manipulation. + +.. function:: slugify + + Converts to lowercase, removes non-word characters (alphanumerics and + underscores) and converts spaces to hyphens. Also strips leading and trailing + whitespace. + + For example:: + + slugify(value) + + If ``value`` is ``"Joel is a slug"``, the output will be ``"joel-is-a-slug"``. + ``django.utils.translation`` ============================ |
