Ticket #18033: 18033-3.diff

File 18033-3.diff, 152.9 KB (added by Claude Paroz, 12 years ago)

Updated to current trunk

  • deleted file django/views/generic/create_update.py

    From d6f598b2f93e98573656e29c2df3747633d2ade0 Mon Sep 17 00:00:00 2001
    From: Claude Paroz <claude@2xlibre.net>
    Date: Sat, 31 Mar 2012 21:26:00 +0200
    Subject: [PATCH 3/3] Remove function-based generic views
    
    ---
     django/views/generic/create_update.py              |  221 ----
     django/views/generic/date_based.py                 |  376 -------
     django/views/generic/list_detail.py                |  152 ---
     django/views/generic/simple.py                     |   68 --
     docs/glossary.txt                                  |    2 +-
     docs/index.txt                                     |    1 -
     docs/internals/deprecation.txt                     |    2 +-
     docs/intro/tutorial04.txt                          |    2 +-
     docs/misc/api-stability.txt                        |    2 +-
     docs/ref/class-based-views.txt                     |    6 +-
     docs/ref/contrib/messages.txt                      |    4 -
     docs/ref/contrib/sitemaps.txt                      |   20 +-
     docs/ref/generic-views.txt                         | 1112 --------------------
     docs/ref/index.txt                                 |    8 -
     docs/releases/1.1-alpha-1.txt                      |    4 +-
     docs/releases/1.1.txt                              |    4 +-
     docs/releases/1.3-alpha-1.txt                      |    2 +-
     docs/releases/1.3.txt                              |    4 +-
     docs/topics/auth.txt                               |   13 -
     docs/topics/class-based-views.txt                  |    6 +-
     docs/topics/generic-views-migration.txt            |  159 ---
     docs/topics/generic-views.txt                      |  511 ---------
     docs/topics/http/generic-views.txt                 |    2 +-
     docs/topics/http/urls.txt                          |   12 +-
     docs/topics/index.txt                              |    9 -
     tests/regressiontests/special_headers/tests.py     |   10 -
     tests/regressiontests/special_headers/urls.py      |    3 +-
     tests/regressiontests/special_headers/views.py     |    9 +-
     tests/regressiontests/views/generic_urls.py        |   74 +--
     tests/regressiontests/views/tests/__init__.py      |    5 -
     .../views/tests/generic/create_update.py           |  255 -----
     .../views/tests/generic/date_based.py              |  171 ---
     .../views/tests/generic/object_list.py             |   47 -
     .../regressiontests/views/tests/generic/simple.py  |   64 --
     tests/regressiontests/views/tests/shortcuts.py     |   10 -
     tests/regressiontests/views/tests/specials.py      |   10 -
     tests/regressiontests/views/views.py               |   19 -
     37 files changed, 45 insertions(+), 3334 deletions(-)
     delete mode 100644 django/views/generic/create_update.py
     delete mode 100644 django/views/generic/date_based.py
     delete mode 100644 django/views/generic/list_detail.py
     delete mode 100644 django/views/generic/simple.py
     delete mode 100644 docs/ref/generic-views.txt
     delete mode 100644 docs/topics/generic-views-migration.txt
     delete mode 100644 docs/topics/generic-views.txt
     delete mode 100644 tests/regressiontests/views/tests/generic/__init__.py
     delete mode 100644 tests/regressiontests/views/tests/generic/create_update.py
     delete mode 100644 tests/regressiontests/views/tests/generic/date_based.py
     delete mode 100644 tests/regressiontests/views/tests/generic/object_list.py
     delete mode 100644 tests/regressiontests/views/tests/generic/simple.py
    
    diff --git a/django/views/generic/create_update.py b/django/views/generic/create_update.py
    deleted file mode 100644
    index 59760b4..0000000
    + -  
    1 from django.forms.models import ModelFormMetaclass, ModelForm
    2 from django.template import RequestContext, loader
    3 from django.http import Http404, HttpResponse, HttpResponseRedirect
    4 from django.core.xheaders import populate_xheaders
    5 from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
    6 from django.utils.translation import ugettext
    7 from django.contrib.auth.views import redirect_to_login
    8 from django.views.generic import GenericViewError
    9 from django.contrib import messages
    10 
    11 import warnings
    12 warnings.warn(
    13     'Function-based generic views have been deprecated; use class-based views instead.',
    14     DeprecationWarning
    15 )
    16 
    17 
    18 def apply_extra_context(extra_context, context):
    19     """
    20     Adds items from extra_context dict to context.  If a value in extra_context
    21     is callable, then it is called and the result is added to context.
    22     """
    23     for key, value in extra_context.iteritems():
    24         if callable(value):
    25             context[key] = value()
    26         else:
    27             context[key] = value
    28 
    29 def get_model_and_form_class(model, form_class):
    30     """
    31     Returns a model and form class based on the model and form_class
    32     parameters that were passed to the generic view.
    33 
    34     If ``form_class`` is given then its associated model will be returned along
    35     with ``form_class`` itself.  Otherwise, if ``model`` is given, ``model``
    36     itself will be returned along with a ``ModelForm`` class created from
    37     ``model``.
    38     """
    39     if form_class:
    40         return form_class._meta.model, form_class
    41     if model:
    42         # The inner Meta class fails if model = model is used for some reason.
    43         tmp_model = model
    44         # TODO: we should be able to construct a ModelForm without creating
    45         # and passing in a temporary inner class.
    46         class Meta:
    47             model = tmp_model
    48         class_name = model.__name__ + 'Form'
    49         form_class = ModelFormMetaclass(class_name, (ModelForm,), {'Meta': Meta})
    50         return model, form_class
    51     raise GenericViewError("Generic view must be called with either a model or"
    52                            " form_class argument.")
    53 
    54 def redirect(post_save_redirect, obj):
    55     """
    56     Returns a HttpResponseRedirect to ``post_save_redirect``.
    57 
    58     ``post_save_redirect`` should be a string, and can contain named string-
    59     substitution place holders of ``obj`` field names.
    60 
    61     If ``post_save_redirect`` is None, then redirect to ``obj``'s URL returned
    62     by ``get_absolute_url()``.  If ``obj`` has no ``get_absolute_url`` method,
    63     then raise ImproperlyConfigured.
    64 
    65     This function is meant to handle the post_save_redirect parameter to the
    66     ``create_object`` and ``update_object`` views.
    67     """
    68     if post_save_redirect:
    69         return HttpResponseRedirect(post_save_redirect % obj.__dict__)
    70     elif hasattr(obj, 'get_absolute_url'):
    71         return HttpResponseRedirect(obj.get_absolute_url())
    72     else:
    73         raise ImproperlyConfigured(
    74             "No URL to redirect to.  Either pass a post_save_redirect"
    75             " parameter to the generic view or define a get_absolute_url"
    76             " method on the Model.")
    77 
    78 def lookup_object(model, object_id, slug, slug_field):
    79     """
    80     Return the ``model`` object with the passed ``object_id``.  If
    81     ``object_id`` is None, then return the object whose ``slug_field``
    82     equals the passed ``slug``.  If ``slug`` and ``slug_field`` are not passed,
    83     then raise Http404 exception.
    84     """
    85     lookup_kwargs = {}
    86     if object_id:
    87         lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
    88     elif slug and slug_field:
    89         lookup_kwargs['%s__exact' % slug_field] = slug
    90     else:
    91         raise GenericViewError(
    92             "Generic view must be called with either an object_id or a"
    93             " slug/slug_field.")
    94     try:
    95         return model.objects.get(**lookup_kwargs)
    96     except ObjectDoesNotExist:
    97         raise Http404("No %s found for %s"
    98                       % (model._meta.verbose_name, lookup_kwargs))
    99 
    100 def create_object(request, model=None, template_name=None,
    101         template_loader=loader, extra_context=None, post_save_redirect=None,
    102         login_required=False, context_processors=None, form_class=None):
    103     """
    104     Generic object-creation function.
    105 
    106     Templates: ``<app_label>/<model_name>_form.html``
    107     Context:
    108         form
    109             the form for the object
    110     """
    111     if extra_context is None: extra_context = {}
    112     if login_required and not request.user.is_authenticated():
    113         return redirect_to_login(request.path)
    114 
    115     model, form_class = get_model_and_form_class(model, form_class)
    116     if request.method == 'POST':
    117         form = form_class(request.POST, request.FILES)
    118         if form.is_valid():
    119             new_object = form.save()
    120 
    121             msg = ugettext("The %(verbose_name)s was created successfully.") %\
    122                                     {"verbose_name": model._meta.verbose_name}
    123             messages.success(request, msg, fail_silently=True)
    124             return redirect(post_save_redirect, new_object)
    125     else:
    126         form = form_class()
    127 
    128     # Create the template, context, response
    129     if not template_name:
    130         template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
    131     t = template_loader.get_template(template_name)
    132     c = RequestContext(request, {
    133         'form': form,
    134     }, context_processors)
    135     apply_extra_context(extra_context, c)
    136     return HttpResponse(t.render(c))
    137 
    138 def update_object(request, model=None, object_id=None, slug=None,
    139         slug_field='slug', template_name=None, template_loader=loader,
    140         extra_context=None, post_save_redirect=None, login_required=False,
    141         context_processors=None, template_object_name='object',
    142         form_class=None):
    143     """
    144     Generic object-update function.
    145 
    146     Templates: ``<app_label>/<model_name>_form.html``
    147     Context:
    148         form
    149             the form for the object
    150         object
    151             the original object being edited
    152     """
    153     if extra_context is None: extra_context = {}
    154     if login_required and not request.user.is_authenticated():
    155         return redirect_to_login(request.path)
    156 
    157     model, form_class = get_model_and_form_class(model, form_class)
    158     obj = lookup_object(model, object_id, slug, slug_field)
    159 
    160     if request.method == 'POST':
    161         form = form_class(request.POST, request.FILES, instance=obj)
    162         if form.is_valid():
    163             obj = form.save()
    164             msg = ugettext("The %(verbose_name)s was updated successfully.") %\
    165                                     {"verbose_name": model._meta.verbose_name}
    166             messages.success(request, msg, fail_silently=True)
    167             return redirect(post_save_redirect, obj)
    168     else:
    169         form = form_class(instance=obj)
    170 
    171     if not template_name:
    172         template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
    173     t = template_loader.get_template(template_name)
    174     c = RequestContext(request, {
    175         'form': form,
    176         template_object_name: obj,
    177     }, context_processors)
    178     apply_extra_context(extra_context, c)
    179     response = HttpResponse(t.render(c))
    180     populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname))
    181     return response
    182 
    183 def delete_object(request, model, post_delete_redirect, object_id=None,
    184         slug=None, slug_field='slug', template_name=None,
    185         template_loader=loader, extra_context=None, login_required=False,
    186         context_processors=None, template_object_name='object'):
    187     """
    188     Generic object-delete function.
    189 
    190     The given template will be used to confirm deletetion if this view is
    191     fetched using GET; for safty, deletion will only be performed if this
    192     view is POSTed.
    193 
    194     Templates: ``<app_label>/<model_name>_confirm_delete.html``
    195     Context:
    196         object
    197             the original object being deleted
    198     """
    199     if extra_context is None: extra_context = {}
    200     if login_required and not request.user.is_authenticated():
    201         return redirect_to_login(request.path)
    202 
    203     obj = lookup_object(model, object_id, slug, slug_field)
    204 
    205     if request.method == 'POST':
    206         obj.delete()
    207         msg = ugettext("The %(verbose_name)s was deleted.") %\
    208                                     {"verbose_name": model._meta.verbose_name}
    209         messages.success(request, msg, fail_silently=True)
    210         return HttpResponseRedirect(post_delete_redirect)
    211     else:
    212         if not template_name:
    213             template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
    214         t = template_loader.get_template(template_name)
    215         c = RequestContext(request, {
    216             template_object_name: obj,
    217         }, context_processors)
    218         apply_extra_context(extra_context, c)
    219         response = HttpResponse(t.render(c))
    220         populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname))
    221         return response
  • deleted file django/views/generic/date_based.py

    diff --git a/django/views/generic/date_based.py b/django/views/generic/date_based.py
    deleted file mode 100644
    index 75094aa..0000000
    + -  
    1 import datetime
    2 import time
    3 
    4 from django.template import loader, RequestContext
    5 from django.core.exceptions import ObjectDoesNotExist
    6 from django.core.xheaders import populate_xheaders
    7 from django.db.models.fields import DateTimeField
    8 from django.http import Http404, HttpResponse
    9 from django.utils import timezone
    10 
    11 import warnings
    12 warnings.warn(
    13     'Function-based generic views have been deprecated; use class-based views instead.',
    14     DeprecationWarning
    15 )
    16 
    17 
    18 def archive_index(request, queryset, date_field, num_latest=15,
    19         template_name=None, template_loader=loader,
    20         extra_context=None, allow_empty=True, context_processors=None,
    21         mimetype=None, allow_future=False, template_object_name='latest'):
    22     """
    23     Generic top-level archive of date-based objects.
    24 
    25     Templates: ``<app_label>/<model_name>_archive.html``
    26     Context:
    27         date_list
    28             List of years
    29         latest
    30             Latest N (defaults to 15) objects by date
    31     """
    32     if extra_context is None: extra_context = {}
    33     model = queryset.model
    34     if not allow_future:
    35         queryset = queryset.filter(**{'%s__lte' % date_field: timezone.now()})
    36     date_list = queryset.dates(date_field, 'year')[::-1]
    37     if not date_list and not allow_empty:
    38         raise Http404("No %s available" % model._meta.verbose_name)
    39 
    40     if date_list and num_latest:
    41         latest = queryset.order_by('-'+date_field)[:num_latest]
    42     else:
    43         latest = None
    44 
    45     if not template_name:
    46         template_name = "%s/%s_archive.html" % (model._meta.app_label, model._meta.object_name.lower())
    47     t = template_loader.get_template(template_name)
    48     c = RequestContext(request, {
    49         'date_list' : date_list,
    50         template_object_name : latest,
    51     }, context_processors)
    52     for key, value in extra_context.items():
    53         if callable(value):
    54             c[key] = value()
    55         else:
    56             c[key] = value
    57     return HttpResponse(t.render(c), mimetype=mimetype)
    58 
    59 def archive_year(request, year, queryset, date_field, template_name=None,
    60         template_loader=loader, extra_context=None, allow_empty=False,
    61         context_processors=None, template_object_name='object', mimetype=None,
    62         make_object_list=False, allow_future=False):
    63     """
    64     Generic yearly archive view.
    65 
    66     Templates: ``<app_label>/<model_name>_archive_year.html``
    67     Context:
    68         date_list
    69             List of months in this year with objects
    70         year
    71             This year
    72         object_list
    73             List of objects published in the given month
    74             (Only available if make_object_list argument is True)
    75     """
    76     if extra_context is None: extra_context = {}
    77     model = queryset.model
    78     now = timezone.now()
    79 
    80     lookup_kwargs = {'%s__year' % date_field: year}
    81 
    82     # Only bother to check current date if the year isn't in the past and future objects aren't requested.
    83     if int(year) >= now.year and not allow_future:
    84         lookup_kwargs['%s__lte' % date_field] = now
    85     date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month')
    86     if not date_list and not allow_empty:
    87         raise Http404
    88     if make_object_list:
    89         object_list = queryset.filter(**lookup_kwargs)
    90     else:
    91         object_list = []
    92     if not template_name:
    93         template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower())
    94     t = template_loader.get_template(template_name)
    95     c = RequestContext(request, {
    96         'date_list': date_list,
    97         'year': year,
    98         '%s_list' % template_object_name: object_list,
    99     }, context_processors)
    100     for key, value in extra_context.items():
    101         if callable(value):
    102             c[key] = value()
    103         else:
    104             c[key] = value
    105     return HttpResponse(t.render(c), mimetype=mimetype)
    106 
    107 def archive_month(request, year, month, queryset, date_field,
    108         month_format='%b', template_name=None, template_loader=loader,
    109         extra_context=None, allow_empty=False, context_processors=None,
    110         template_object_name='object', mimetype=None, allow_future=False):
    111     """
    112     Generic monthly archive view.
    113 
    114     Templates: ``<app_label>/<model_name>_archive_month.html``
    115     Context:
    116         date_list:
    117             List of days in this month with objects
    118         month:
    119             (date) this month
    120         next_month:
    121             (date) the first day of the next month, or None if the next month is in the future
    122         previous_month:
    123             (date) the first day of the previous month
    124         object_list:
    125             list of objects published in the given month
    126     """
    127     if extra_context is None: extra_context = {}
    128     try:
    129         tt = time.strptime("%s-%s" % (year, month), '%s-%s' % ('%Y', month_format))
    130         date = datetime.date(*tt[:3])
    131     except ValueError:
    132         raise Http404
    133 
    134     model = queryset.model
    135     now = timezone.now()
    136 
    137     # Calculate first and last day of month, for use in a date-range lookup.
    138     first_day = date.replace(day=1)
    139     if first_day.month == 12:
    140         last_day = first_day.replace(year=first_day.year + 1, month=1)
    141     else:
    142         last_day = first_day.replace(month=first_day.month + 1)
    143     lookup_kwargs = {
    144         '%s__gte' % date_field: first_day,
    145         '%s__lt' % date_field: last_day,
    146     }
    147 
    148     # Only bother to check current date if the month isn't in the past and future objects are requested.
    149     if last_day >= now.date() and not allow_future:
    150         lookup_kwargs['%s__lte' % date_field] = now
    151     object_list = queryset.filter(**lookup_kwargs)
    152     date_list = object_list.dates(date_field, 'day')
    153     if not object_list and not allow_empty:
    154         raise Http404
    155 
    156     # Calculate the next month, if applicable.
    157     if allow_future:
    158         next_month = last_day
    159     elif last_day <= datetime.date.today():
    160         next_month = last_day
    161     else:
    162         next_month = None
    163 
    164     # Calculate the previous month
    165     if first_day.month == 1:
    166         previous_month = first_day.replace(year=first_day.year-1,month=12)
    167     else:
    168         previous_month = first_day.replace(month=first_day.month-1)
    169 
    170     if not template_name:
    171         template_name = "%s/%s_archive_month.html" % (model._meta.app_label, model._meta.object_name.lower())
    172     t = template_loader.get_template(template_name)
    173     c = RequestContext(request, {
    174         'date_list': date_list,
    175         '%s_list' % template_object_name: object_list,
    176         'month': date,
    177         'next_month': next_month,
    178         'previous_month': previous_month,
    179     }, context_processors)
    180     for key, value in extra_context.items():
    181         if callable(value):
    182             c[key] = value()
    183         else:
    184             c[key] = value
    185     return HttpResponse(t.render(c), mimetype=mimetype)
    186 
    187 def archive_week(request, year, week, queryset, date_field,
    188         template_name=None, template_loader=loader,
    189         extra_context=None, allow_empty=True, context_processors=None,
    190         template_object_name='object', mimetype=None, allow_future=False):
    191     """
    192     Generic weekly archive view.
    193 
    194     Templates: ``<app_label>/<model_name>_archive_week.html``
    195     Context:
    196         week:
    197             (date) this week
    198         object_list:
    199             list of objects published in the given week
    200     """
    201     if extra_context is None: extra_context = {}
    202     try:
    203         tt = time.strptime(year+'-0-'+week, '%Y-%w-%U')
    204         date = datetime.date(*tt[:3])
    205     except ValueError:
    206         raise Http404
    207 
    208     model = queryset.model
    209     now = timezone.now()
    210 
    211     # Calculate first and last day of week, for use in a date-range lookup.
    212     first_day = date
    213     last_day = date + datetime.timedelta(days=7)
    214     lookup_kwargs = {
    215         '%s__gte' % date_field: first_day,
    216         '%s__lt' % date_field: last_day,
    217     }
    218 
    219     # Only bother to check current date if the week isn't in the past and future objects aren't requested.
    220     if last_day >= now.date() and not allow_future:
    221         lookup_kwargs['%s__lte' % date_field] = now
    222     object_list = queryset.filter(**lookup_kwargs)
    223     if not object_list and not allow_empty:
    224         raise Http404
    225     if not template_name:
    226         template_name = "%s/%s_archive_week.html" % (model._meta.app_label, model._meta.object_name.lower())
    227     t = template_loader.get_template(template_name)
    228     c = RequestContext(request, {
    229         '%s_list' % template_object_name: object_list,
    230         'week': date,
    231     })
    232     for key, value in extra_context.items():
    233         if callable(value):
    234             c[key] = value()
    235         else:
    236             c[key] = value
    237     return HttpResponse(t.render(c), mimetype=mimetype)
    238 
    239 def archive_day(request, year, month, day, queryset, date_field,
    240         month_format='%b', day_format='%d', template_name=None,
    241         template_loader=loader, extra_context=None, allow_empty=False,
    242         context_processors=None, template_object_name='object',
    243         mimetype=None, allow_future=False):
    244     """
    245     Generic daily archive view.
    246 
    247     Templates: ``<app_label>/<model_name>_archive_day.html``
    248     Context:
    249         object_list:
    250             list of objects published that day
    251         day:
    252             (datetime) the day
    253         previous_day
    254             (datetime) the previous day
    255         next_day
    256             (datetime) the next day, or None if the current day is today
    257     """
    258     if extra_context is None: extra_context = {}
    259     try:
    260         tt = time.strptime('%s-%s-%s' % (year, month, day),
    261                            '%s-%s-%s' % ('%Y', month_format, day_format))
    262         date = datetime.date(*tt[:3])
    263     except ValueError:
    264         raise Http404
    265 
    266     model = queryset.model
    267     now = timezone.now()
    268 
    269     if isinstance(model._meta.get_field(date_field), DateTimeField):
    270         lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))}
    271     else:
    272         lookup_kwargs = {date_field: date}
    273 
    274     # Only bother to check current date if the date isn't in the past and future objects aren't requested.
    275     if date >= now.date() and not allow_future:
    276         lookup_kwargs['%s__lte' % date_field] = now
    277     object_list = queryset.filter(**lookup_kwargs)
    278     if not allow_empty and not object_list:
    279         raise Http404
    280 
    281     # Calculate the next day, if applicable.
    282     if allow_future:
    283         next_day = date + datetime.timedelta(days=1)
    284     elif date < datetime.date.today():
    285         next_day = date + datetime.timedelta(days=1)
    286     else:
    287         next_day = None
    288 
    289     if not template_name:
    290         template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower())
    291     t = template_loader.get_template(template_name)
    292     c = RequestContext(request, {
    293         '%s_list' % template_object_name: object_list,
    294         'day': date,
    295         'previous_day': date - datetime.timedelta(days=1),
    296         'next_day': next_day,
    297     }, context_processors)
    298     for key, value in extra_context.items():
    299         if callable(value):
    300             c[key] = value()
    301         else:
    302             c[key] = value
    303     return HttpResponse(t.render(c), mimetype=mimetype)
    304 
    305 def archive_today(request, **kwargs):
    306     """
    307     Generic daily archive view for today. Same as archive_day view.
    308     """
    309     today = datetime.date.today()
    310     kwargs.update({
    311         'year': str(today.year),
    312         'month': today.strftime('%b').lower(),
    313         'day': str(today.day),
    314     })
    315     return archive_day(request, **kwargs)
    316 
    317 def object_detail(request, year, month, day, queryset, date_field,
    318         month_format='%b', day_format='%d', object_id=None, slug=None,
    319         slug_field='slug', template_name=None, template_name_field=None,
    320         template_loader=loader, extra_context=None, context_processors=None,
    321         template_object_name='object', mimetype=None, allow_future=False):
    322     """
    323     Generic detail view from year/month/day/slug or year/month/day/id structure.
    324 
    325     Templates: ``<app_label>/<model_name>_detail.html``
    326     Context:
    327         object:
    328             the object to be detailed
    329     """
    330     if extra_context is None: extra_context = {}
    331     try:
    332         tt = time.strptime('%s-%s-%s' % (year, month, day),
    333                            '%s-%s-%s' % ('%Y', month_format, day_format))
    334         date = datetime.date(*tt[:3])
    335     except ValueError:
    336         raise Http404
    337 
    338     model = queryset.model
    339     now = timezone.now()
    340 
    341     if isinstance(model._meta.get_field(date_field), DateTimeField):
    342         lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))}
    343     else:
    344         lookup_kwargs = {date_field: date}
    345 
    346     # Only bother to check current date if the date isn't in the past and future objects aren't requested.
    347     if date >= now.date() and not allow_future:
    348         lookup_kwargs['%s__lte' % date_field] = now
    349     if object_id:
    350         lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
    351     elif slug and slug_field:
    352         lookup_kwargs['%s__exact' % slug_field] = slug
    353     else:
    354         raise AttributeError("Generic detail view must be called with either an object_id or a slug/slugfield")
    355     try:
    356         obj = queryset.get(**lookup_kwargs)
    357     except ObjectDoesNotExist:
    358         raise Http404("No %s found for" % model._meta.verbose_name)
    359     if not template_name:
    360         template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower())
    361     if template_name_field:
    362         template_name_list = [getattr(obj, template_name_field), template_name]
    363         t = template_loader.select_template(template_name_list)
    364     else:
    365         t = template_loader.get_template(template_name)
    366     c = RequestContext(request, {
    367         template_object_name: obj,
    368     }, context_processors)
    369     for key, value in extra_context.items():
    370         if callable(value):
    371             c[key] = value()
    372         else:
    373             c[key] = value
    374     response = HttpResponse(t.render(c), mimetype=mimetype)
    375     populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
    376     return response
  • deleted file django/views/generic/list_detail.py

    diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py
    deleted file mode 100644
    index 22414ae..0000000
    + -  
    1 from django.template import loader, RequestContext
    2 from django.http import Http404, HttpResponse
    3 from django.core.xheaders import populate_xheaders
    4 from django.core.paginator import Paginator, InvalidPage
    5 from django.core.exceptions import ObjectDoesNotExist
    6 
    7 import warnings
    8 warnings.warn(
    9     'Function-based generic views have been deprecated; use class-based views instead.',
    10     DeprecationWarning
    11 )
    12 
    13 
    14 def object_list(request, queryset, paginate_by=None, page=None,
    15         allow_empty=True, template_name=None, template_loader=loader,
    16         extra_context=None, context_processors=None, template_object_name='object',
    17         mimetype=None):
    18     """
    19     Generic list of objects.
    20 
    21     Templates: ``<app_label>/<model_name>_list.html``
    22     Context:
    23         object_list
    24             list of objects
    25         is_paginated
    26             are the results paginated?
    27         results_per_page
    28             number of objects per page (if paginated)
    29         has_next
    30             is there a next page?
    31         has_previous
    32             is there a prev page?
    33         page
    34             the current page
    35         next
    36             the next page
    37         previous
    38             the previous page
    39         pages
    40             number of pages, total
    41         hits
    42             number of objects, total
    43         last_on_page
    44             the result number of the last of object in the
    45             object_list (1-indexed)
    46         first_on_page
    47             the result number of the first object in the
    48             object_list (1-indexed)
    49         page_range:
    50             A list of the page numbers (1-indexed).
    51     """
    52     if extra_context is None: extra_context = {}
    53     queryset = queryset._clone()
    54     if paginate_by:
    55         paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
    56         if not page:
    57             page = request.GET.get('page', 1)
    58         try:
    59             page_number = int(page)
    60         except ValueError:
    61             if page == 'last':
    62                 page_number = paginator.num_pages
    63             else:
    64                 # Page is not 'last', nor can it be converted to an int.
    65                 raise Http404
    66         try:
    67             page_obj = paginator.page(page_number)
    68         except InvalidPage:
    69             raise Http404
    70         c = RequestContext(request, {
    71             '%s_list' % template_object_name: page_obj.object_list,
    72             'paginator': paginator,
    73             'page_obj': page_obj,
    74             'is_paginated': page_obj.has_other_pages(),
    75 
    76             # Legacy template context stuff. New templates should use page_obj
    77             # to access this instead.
    78             'results_per_page': paginator.per_page,
    79             'has_next': page_obj.has_next(),
    80             'has_previous': page_obj.has_previous(),
    81             'page': page_obj.number,
    82             'next': page_obj.next_page_number(),
    83             'previous': page_obj.previous_page_number(),
    84             'first_on_page': page_obj.start_index(),
    85             'last_on_page': page_obj.end_index(),
    86             'pages': paginator.num_pages,
    87             'hits': paginator.count,
    88             'page_range': paginator.page_range,
    89         }, context_processors)
    90     else:
    91         c = RequestContext(request, {
    92             '%s_list' % template_object_name: queryset,
    93             'paginator': None,
    94             'page_obj': None,
    95             'is_paginated': False,
    96         }, context_processors)
    97         if not allow_empty and len(queryset) == 0:
    98             raise Http404
    99     for key, value in extra_context.items():
    100         if callable(value):
    101             c[key] = value()
    102         else:
    103             c[key] = value
    104     if not template_name:
    105         model = queryset.model
    106         template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
    107     t = template_loader.get_template(template_name)
    108     return HttpResponse(t.render(c), mimetype=mimetype)
    109 
    110 def object_detail(request, queryset, object_id=None, slug=None,
    111         slug_field='slug', template_name=None, template_name_field=None,
    112         template_loader=loader, extra_context=None,
    113         context_processors=None, template_object_name='object',
    114         mimetype=None):
    115     """
    116     Generic detail of an object.
    117 
    118     Templates: ``<app_label>/<model_name>_detail.html``
    119     Context:
    120         object
    121             the object
    122     """
    123     if extra_context is None: extra_context = {}
    124     model = queryset.model
    125     if object_id:
    126         queryset = queryset.filter(pk=object_id)
    127     elif slug and slug_field:
    128         queryset = queryset.filter(**{slug_field: slug})
    129     else:
    130         raise AttributeError("Generic detail view must be called with either an object_id or a slug/slug_field.")
    131     try:
    132         obj = queryset.get()
    133     except ObjectDoesNotExist:
    134         raise Http404("No %s found matching the query" % (model._meta.verbose_name))
    135     if not template_name:
    136         template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower())
    137     if template_name_field:
    138         template_name_list = [getattr(obj, template_name_field), template_name]
    139         t = template_loader.select_template(template_name_list)
    140     else:
    141         t = template_loader.get_template(template_name)
    142     c = RequestContext(request, {
    143         template_object_name: obj,
    144     }, context_processors)
    145     for key, value in extra_context.items():
    146         if callable(value):
    147             c[key] = value()
    148         else:
    149             c[key] = value
    150     response = HttpResponse(t.render(c), mimetype=mimetype)
    151     populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
    152     return response
  • deleted file django/views/generic/simple.py

    diff --git a/django/views/generic/simple.py b/django/views/generic/simple.py
    deleted file mode 100644
    index f63860a..0000000
    + -  
    1 from django.template import loader, RequestContext
    2 from django.http import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseGone
    3 from django.utils.log import getLogger
    4 
    5 import warnings
    6 warnings.warn(
    7     'Function-based generic views have been deprecated; use class-based views instead.',
    8     DeprecationWarning
    9 )
    10 
    11 logger = getLogger('django.request')
    12 
    13 
    14 def direct_to_template(request, template, extra_context=None, mimetype=None, **kwargs):
    15     """
    16     Render a given template with any extra URL parameters in the context as
    17     ``{{ params }}``.
    18     """
    19     if extra_context is None: extra_context = {}
    20     dictionary = {'params': kwargs}
    21     for key, value in extra_context.items():
    22         if callable(value):
    23             dictionary[key] = value()
    24         else:
    25             dictionary[key] = value
    26     c = RequestContext(request, dictionary)
    27     t = loader.get_template(template)
    28     return HttpResponse(t.render(c), content_type=mimetype)
    29 
    30 def redirect_to(request, url, permanent=True, query_string=False, **kwargs):
    31     """
    32     Redirect to a given URL.
    33 
    34     The given url may contain dict-style string formatting, which will be
    35     interpolated against the params in the URL.  For example, to redirect from
    36     ``/foo/<id>/`` to ``/bar/<id>/``, you could use the following URLconf::
    37 
    38         urlpatterns = patterns('',
    39             ('^foo/(?P<id>\d+)/$', 'django.views.generic.simple.redirect_to', {'url' : '/bar/%(id)s/'}),
    40         )
    41 
    42     If the given url is ``None``, a HttpResponseGone (410) will be issued.
    43 
    44     If the ``permanent`` argument is False, then the response will have a 302
    45     HTTP status code. Otherwise, the status code will be 301.
    46 
    47     If the ``query_string`` argument is True, then the GET query string
    48     from the request is appended to the URL.
    49 
    50     """
    51     args = request.META.get('QUERY_STRING', '')
    52 
    53     if url is not None:
    54         if kwargs:
    55             url = url % kwargs
    56 
    57         if args and query_string:
    58             url = "%s?%s" % (url, args)
    59 
    60         klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect
    61         return klass(url)
    62     else:
    63         logger.warning('Gone: %s', request.path,
    64                     extra={
    65                         'status_code': 410,
    66                         'request': request
    67                     })
    68         return HttpResponseGone()
  • docs/glossary.txt

    diff --git a/docs/glossary.txt b/docs/glossary.txt
    index 15776ce..4d0f8b9 100644
    a b Glossary  
    1616        A higher-order :term:`view` function that provides an abstract/generic
    1717        implementation of a common idiom or pattern found in view development.
    1818
    19         See :doc:`/ref/generic-views`.
     19        See :doc:`/ref/class-based-views`.
    2020
    2121    model
    2222        Models store your application's data.
  • docs/index.txt

    diff --git a/docs/index.txt b/docs/index.txt
    index 66c8e73..d596511 100644
    a b Other batteries included  
    195195* :doc:`Unicode in Django <ref/unicode>`
    196196* :doc:`Web design helpers <ref/contrib/webdesign>`
    197197* :doc:`Validators <ref/validators>`
    198 * Function-based generic views (Deprecated) :doc:`Overview<topics/generic-views>` | :doc:`Built-in generic views<ref/generic-views>` | :doc:`Migration guide<topics/generic-views-migration>`
    199198
    200199The Django open-source project
    201200==============================
  • docs/internals/deprecation.txt

    diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
    index e54adf0..e3a5e1a 100644
    a b these changes.  
    134134
    135135* The function-based generic view modules will be removed in favor of their
    136136  class-based equivalents, outlined :doc:`here
    137   </topics/generic-views-migration>`:
     137  </topics/class-based-views>`:
    138138
    139139* The :class:`~django.core.servers.basehttp.AdminMediaHandler` will be
    140140  removed.  In its place use
  • docs/intro/tutorial04.txt

    diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt
    index 85d54c3..8a9f4ee 100644
    a b function anymore -- generic views can be (and are) used multiple times  
    320320Run the server, and use your new polling app based on generic views.
    321321
    322322For full details on generic views, see the :doc:`generic views documentation
    323 </topics/http/generic-views>`.
     323</topics/class-based-views>`.
    324324
    325325Coming soon
    326326===========
  • docs/misc/api-stability.txt

    diff --git a/docs/misc/api-stability.txt b/docs/misc/api-stability.txt
    index 75fa6b4..5517026 100644
    a b of 1.0. This includes these APIs:  
    5454- :doc:`HTTP request/response handling </topics/http/index>`, including file
    5555  uploads, middleware, sessions, URL resolution, view, and shortcut APIs.
    5656
    57 - :doc:`Generic views </topics/http/generic-views>`.
     57- :doc:`Generic views </topics/class-based-views>`.
    5858
    5959- :doc:`Internationalization </topics/i18n/index>`.
    6060
  • docs/ref/class-based-views.txt

    diff --git a/docs/ref/class-based-views.txt b/docs/ref/class-based-views.txt
    index 5223aee..aa3f8f7 100644
    a b Class-based generic views  
    66
    77.. note::
    88    Prior to Django 1.3, generic views were implemented as functions. The
    9     function-based implementation has been deprecated in favor of the
     9    function-based implementation has been removed in favor of the
    1010    class-based approach described here.
    1111
    12     For details on the previous generic views implementation,
    13     see the :doc:`topic guide </topics/generic-views>` and
    14     :doc:`detailed reference </ref/generic-views>`.
    15 
    1612Writing Web applications can be monotonous, because we repeat certain patterns
    1713again and again. Django tries to take away some of that monotony at the model
    1814and template layers, but Web developers also experience this boredom at the view
  • docs/ref/contrib/messages.txt

    diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt
    index 223a162..4ab9734 100644
    a b example::  
    274274                         fail_silently=True)
    275275    messages.info(request, 'Hello world.', fail_silently=True)
    276276
    277 Internally, Django uses this functionality in the create, update, and delete
    278 :doc:`generic views </topics/http/generic-views>` so that they work even if the
    279 message framework is disabled.
    280 
    281277.. note::
    282278   Setting ``fail_silently=True`` only hides the ``MessageFailure`` that would
    283279   otherwise occur when the messages framework disabled and one attempts to
  • docs/ref/contrib/sitemaps.txt

    diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt
    index 64400bc..d775092 100644
    a b The sitemap framework provides a couple convenience classes for common cases:  
    240240
    241241.. class:: GenericSitemap
    242242
    243     The :class:`django.contrib.sitemaps.GenericSitemap` class works with any
    244     :doc:`generic views </ref/generic-views>` you already have.
    245     To use it, create an instance, passing in the same :data:`info_dict` you pass to
    246     the generic views. The only requirement is that the dictionary have a
    247     :data:`queryset` entry. It may also have a :data:`date_field` entry that specifies a
    248     date field for objects retrieved from the :data:`queryset`. This will be used for
    249     the :attr:`~Sitemap.lastmod` attribute in the generated sitemap. You may
    250     also pass :attr:`~Sitemap.priority` and :attr:`~Sitemap.changefreq`
    251     keyword arguments to the :class:`~django.contrib.sitemaps.GenericSitemap`
    252     constructor to specify these attributes for all URLs.
     243    The :class:`django.contrib.sitemaps.GenericSitemap` class allows you to
     244    create a sitemap by passing it a dictionary which has to contain at least
     245    a :data:`queryset` entry. This queryset will be used to generate the items
     246    of the sitemap. It may also have a :data:`date_field` entry that
     247    specifies a date field for objects retrieved from the :data:`queryset`.
     248    This will be used for the :attr:`~Sitemap.lastmod` attribute in the
     249    generated sitemap. You may also pass :attr:`~Sitemap.priority` and
     250    :attr:`~Sitemap.changefreq` keyword arguments to the
     251    :class:`~django.contrib.sitemaps.GenericSitemap`  constructor to specify
     252    these attributes for all URLs.
    253253
    254254Example
    255255-------
  • deleted file docs/ref/generic-views.txt

    diff --git a/docs/ref/generic-views.txt b/docs/ref/generic-views.txt
    deleted file mode 100644
    index eb65107..0000000
    + -  
    1 =============
    2 Generic views
    3 =============
    4 
    5 
    6 .. versionchanged:: 1.3
    7 
    8 .. note::
    9 
    10     From Django 1.3, function-based generic views have been deprecated in favor
    11     of a class-based approach, described in the class-based views :doc:`topic
    12     guide </topics/class-based-views>` and :doc:`detailed reference
    13     </ref/class-based-views>`.
    14 
    15 Writing Web applications can be monotonous, because we repeat certain patterns
    16 again and again. In Django, the most common of these patterns have been
    17 abstracted into "generic views" that let you quickly provide common views of
    18 an object without actually needing to write any Python code.
    19 
    20 A general introduction to generic views can be found in the :doc:`topic guide
    21 </topics/generic-views>`.
    22 
    23 This reference contains details of Django's built-in generic views, along with
    24 a list of all keyword arguments that a generic view expects. Remember that
    25 arguments may either come from the URL pattern or from the ``extra_context``
    26 additional-information dictionary.
    27 
    28 Most generic views require the ``queryset`` key, which is a ``QuerySet``
    29 instance; see :doc:`/topics/db/queries` for more information about ``QuerySet``
    30 objects.
    31 
    32 .. module:: django.views.generic.simple
    33 
    34 "Simple" generic views
    35 ======================
    36 
    37 The ``django.views.generic.simple`` module contains simple views to handle a
    38 couple of common cases: rendering a template when no view logic is needed,
    39 and issuing a redirect.
    40 
    41 ``django.views.generic.simple.direct_to_template``
    42 --------------------------------------------------
    43 
    44 **Description:**
    45 
    46 Renders a given template, passing it a ``{{ params }}`` template variable,
    47 which is a dictionary of the parameters captured in the URL.
    48 
    49 **Required arguments:**
    50 
    51 * ``template``: The full name of a template to use.
    52 
    53 **Optional arguments:**
    54 
    55 * ``extra_context``: A dictionary of values to add to the template
    56   context. By default, this is an empty dictionary. If a value in the
    57   dictionary is callable, the generic view will call it
    58   just before rendering the template.
    59 
    60 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    61   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    62 
    63 **Example:**
    64 
    65 Given the following URL patterns::
    66 
    67     from django.views.generic.simple import direct_to_template
    68 
    69     urlpatterns = patterns('',
    70         (r'^foo/$',             direct_to_template, {'template': 'foo_index.html'}),
    71         (r'^foo/(?P<id>\d+)/$', direct_to_template, {'template': 'foo_detail.html'}),
    72     )
    73 
    74 ... a request to ``/foo/`` would render the template ``foo_index.html``, and a
    75 request to ``/foo/15/`` would render the ``foo_detail.html`` with a context
    76 variable ``{{ params.id }}`` that is set to ``15``.
    77 
    78 ``django.views.generic.simple.redirect_to``
    79 -------------------------------------------
    80 
    81 **Description:**
    82 
    83 Redirects to a given URL.
    84 
    85 The given URL may contain dictionary-style string formatting, which will be
    86 interpolated against the parameters captured in the URL. Because keyword
    87 interpolation is *always* done (even if no arguments are passed in), any ``"%"``
    88 characters in the URL must be written as ``"%%"`` so that Python will convert
    89 them to a single percent sign on output.
    90 
    91 If the given URL is ``None``, Django will return an ``HttpResponseGone`` (410).
    92 
    93 **Required arguments:**
    94 
    95 * ``url``: The URL to redirect to, as a string. Or ``None`` to raise a 410
    96   (Gone) HTTP error.
    97 
    98 **Optional arguments:**
    99 
    100 * ``permanent``: Whether the redirect should be permanent. The only
    101   difference here is the HTTP status code returned. If ``True``, then the
    102   redirect will use status code 301. If ``False``, then the redirect will
    103   use status code 302. By default, ``permanent`` is ``True``.
    104 
    105 * ``query_string``: Whether to pass along the GET query string to
    106   the new location. If ``True``, then the query string is appended
    107   to the URL. If ``False``, then the query string is discarded. By
    108   default, ``query_string`` is ``False``.
    109 
    110 .. versionadded:: 1.3
    111     The ``query_string`` keyword argument is new in Django 1.3.
    112 
    113 **Example:**
    114 
    115 This example issues a permanent redirect (HTTP status code 301) from
    116 ``/foo/<id>/`` to ``/bar/<id>/``::
    117 
    118     from django.views.generic.simple import redirect_to
    119 
    120     urlpatterns = patterns('',
    121         ('^foo/(?P<id>\d+)/$', redirect_to, {'url': '/bar/%(id)s/'}),
    122     )
    123 
    124 This example issues a non-permanent redirect (HTTP status code 302) from
    125 ``/foo/<id>/`` to ``/bar/<id>/``::
    126 
    127     from django.views.generic.simple import redirect_to
    128 
    129     urlpatterns = patterns('',
    130         ('^foo/(?P<id>\d+)/$', redirect_to, {'url': '/bar/%(id)s/', 'permanent': False}),
    131     )
    132 
    133 This example returns a 410 HTTP error for requests to ``/bar/``::
    134 
    135     from django.views.generic.simple import redirect_to
    136 
    137     urlpatterns = patterns('',
    138         ('^bar/$', redirect_to, {'url': None}),
    139     )
    140 
    141 This example shows how ``"%"`` characters must be written in the URL in order
    142 to avoid confusion with Python's string formatting markers. If the redirect
    143 string is written as ``"%7Ejacob/"`` (with only a single ``%``), an exception would be raised::
    144 
    145     from django.views.generic.simple import redirect_to
    146 
    147     urlpatterns = patterns('',
    148         ('^bar/$', redirect_to, {'url': '%%7Ejacob.'}),
    149     )
    150 
    151 .. module:: django.views.generic.date_based
    152 
    153 Date-based generic views
    154 ========================
    155 
    156 Date-based generic views (in the module ``django.views.generic.date_based``)
    157 are views for displaying drilldown pages for date-based data.
    158 
    159 ``django.views.generic.date_based.archive_index``
    160 -------------------------------------------------
    161 
    162 **Description:**
    163 
    164 A top-level index page showing the "latest" objects, by date. Objects with
    165 a date in the *future* are not included unless you set ``allow_future`` to
    166 ``True``.
    167 
    168 **Required arguments:**
    169 
    170 * ``queryset``: A ``QuerySet`` of objects for which the archive serves.
    171 
    172 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    173   the ``QuerySet``'s model that the date-based archive should use to
    174   determine the objects on the page.
    175 
    176 **Optional arguments:**
    177 
    178 * ``num_latest``: The number of latest objects to send to the template
    179   context. By default, it's 15.
    180 
    181 * ``template_name``: The full name of a template to use in rendering the
    182   page. This lets you override the default template name (see below).
    183 
    184 * ``template_loader``: The template loader to use when loading the
    185   template. By default, it's ``django.template.loader``.
    186 
    187 * ``extra_context``: A dictionary of values to add to the template
    188   context. By default, this is an empty dictionary. If a value in the
    189   dictionary is callable, the generic view will call it
    190   just before rendering the template.
    191 
    192 * ``allow_empty``: A boolean specifying whether to display the page if no
    193   objects are available. If this is ``False`` and no objects are available,
    194   the view will raise a 404 instead of displaying an empty page. By
    195   default, this is ``True``.
    196 
    197 * ``context_processors``: A list of template-context processors to apply to
    198   the view's template.
    199 
    200 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    201   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    202 
    203 * ``allow_future``: A boolean specifying whether to include "future"
    204   objects on this page, where "future" means objects in which the field
    205   specified in ``date_field`` is greater than the current date/time. By
    206   default, this is ``False``.
    207 
    208 * ``template_object_name``: Designates the name of the template variable
    209   to use in the template context. By default, this is ``'latest'``.
    210 
    211 **Template name:**
    212 
    213 If ``template_name`` isn't specified, this view will use the template
    214 ``<app_label>/<model_name>_archive.html`` by default, where:
    215 
    216 * ``<model_name>`` is your model's name in all lowercase. For a model
    217   ``StaffMember``, that'd be ``staffmember``.
    218 
    219 * ``<app_label>`` is the right-most part of the full Python path to
    220   your model's app. For example, if your model lives in
    221   ``apps/blog/models.py``, that'd be ``blog``.
    222 
    223 **Template context:**
    224 
    225 In addition to ``extra_context``, the template's context will be:
    226 
    227 * ``date_list``: A ``DateQuerySet`` object containing all years that have
    228   have objects available according to ``queryset``, represented as
    229   ``datetime.datetime`` objects. These are ordered in reverse. This is
    230   equivalent to ``queryset.dates(date_field, 'year')[::-1]``.
    231 
    232 * ``latest``: The ``num_latest`` objects in the system, ordered descending
    233   by ``date_field``. For example, if ``num_latest`` is ``10``, then
    234   ``latest`` will be a list of the latest 10 objects in ``queryset``.
    235 
    236   This variable's name depends on the ``template_object_name`` parameter,
    237   which is ``'latest'`` by default. If ``template_object_name`` is
    238   ``'foo'``, this variable's name will be ``foo``.
    239 
    240 ``django.views.generic.date_based.archive_year``
    241 ------------------------------------------------
    242 
    243 **Description:**
    244 
    245 A yearly archive page showing all available months in a given year. Objects
    246 with a date in the *future* are not displayed unless you set ``allow_future``
    247 to ``True``.
    248 
    249 **Required arguments:**
    250 
    251 * ``year``: The four-digit year for which the archive serves.
    252 
    253 * ``queryset``: A ``QuerySet`` of objects for which the archive serves.
    254 
    255 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    256   the ``QuerySet``'s model that the date-based archive should use to
    257   determine the objects on the page.
    258 
    259 **Optional arguments:**
    260 
    261 * ``template_name``: The full name of a template to use in rendering the
    262   page. This lets you override the default template name (see below).
    263 
    264 * ``template_loader``: The template loader to use when loading the
    265   template. By default, it's ``django.template.loader``.
    266 
    267 * ``extra_context``: A dictionary of values to add to the template
    268   context. By default, this is an empty dictionary. If a value in the
    269   dictionary is callable, the generic view will call it
    270   just before rendering the template.
    271 
    272 * ``allow_empty``: A boolean specifying whether to display the page if no
    273   objects are available. If this is ``False`` and no objects are available,
    274   the view will raise a 404 instead of displaying an empty page. By
    275   default, this is ``False``.
    276 
    277 * ``context_processors``: A list of template-context processors to apply to
    278   the view's template.
    279 
    280 * ``template_object_name``:  Designates the name of the template variable
    281   to use in the template context. By default, this is ``'object'``. The
    282   view will append ``'_list'`` to the value of this parameter in
    283   determining the variable's name.
    284 
    285 * ``make_object_list``: A boolean specifying whether to retrieve the full
    286   list of objects for this year and pass those to the template. If ``True``,
    287   this list of objects will be made available to the template as
    288   ``object_list``. (The name ``object_list`` may be different; see the docs
    289   for ``object_list`` in the "Template context" section below.) By default,
    290   this is ``False``.
    291 
    292 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    293   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    294 
    295 * ``allow_future``: A boolean specifying whether to include "future"
    296   objects on this page, where "future" means objects in which the field
    297   specified in ``date_field`` is greater than the current date/time. By
    298   default, this is ``False``.
    299 
    300 **Template name:**
    301 
    302 If ``template_name`` isn't specified, this view will use the template
    303 ``<app_label>/<model_name>_archive_year.html`` by default.
    304 
    305 **Template context:**
    306 
    307 In addition to ``extra_context``, the template's context will be:
    308 
    309 * ``date_list``: A ``DateQuerySet`` object containing all months that have
    310   have objects available according to ``queryset``, represented as
    311   ``datetime.datetime`` objects, in ascending order.
    312 
    313 * ``year``: The given year, as a four-character string.
    314 
    315 * ``object_list``: If the ``make_object_list`` parameter is ``True``, this
    316   will be set to a list of objects available for the given year, ordered by
    317   the date field. This variable's name depends on the
    318   ``template_object_name`` parameter, which is ``'object'`` by default. If
    319   ``template_object_name`` is ``'foo'``, this variable's name will be
    320   ``foo_list``.
    321 
    322   If ``make_object_list`` is ``False``, ``object_list`` will be passed to
    323   the template as an empty list.
    324 
    325 ``django.views.generic.date_based.archive_month``
    326 -------------------------------------------------
    327 
    328 **Description:**
    329 
    330 A monthly archive page showing all objects in a given month. Objects with a
    331 date in the *future* are not displayed unless you set ``allow_future`` to
    332 ``True``.
    333 
    334 **Required arguments:**
    335 
    336 * ``year``: The four-digit year for which the archive serves (a string).
    337 
    338 * ``month``: The month for which the archive serves, formatted according to
    339   the ``month_format`` argument.
    340 
    341 * ``queryset``: A ``QuerySet`` of objects for which the archive serves.
    342 
    343 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    344   the ``QuerySet``'s model that the date-based archive should use to
    345   determine the objects on the page.
    346 
    347 **Optional arguments:**
    348 
    349 * ``month_format``: A format string that regulates what format the ``month``
    350   parameter uses. This should be in the syntax accepted by Python's
    351   :func:`~time.strftime`. It's set to ``"%b"`` by default, which is a
    352   three-letter month abbreviation. To change it to use numbers, use
    353   ``"%m"``.
    354 
    355 * ``template_name``: The full name of a template to use in rendering the
    356   page. This lets you override the default template name (see below).
    357 
    358 * ``template_loader``: The template loader to use when loading the
    359   template. By default, it's ``django.template.loader``.
    360 
    361 * ``extra_context``: A dictionary of values to add to the template
    362   context. By default, this is an empty dictionary. If a value in the
    363   dictionary is callable, the generic view will call it
    364   just before rendering the template.
    365 
    366 * ``allow_empty``: A boolean specifying whether to display the page if no
    367   objects are available. If this is ``False`` and no objects are available,
    368   the view will raise a 404 instead of displaying an empty page. By
    369   default, this is ``False``.
    370 
    371 * ``context_processors``: A list of template-context processors to apply to
    372   the view's template.
    373 
    374 * ``template_object_name``:  Designates the name of the template variable
    375   to use in the template context. By default, this is ``'object'``. The
    376   view will append ``'_list'`` to the value of this parameter in
    377   determining the variable's name.
    378 
    379 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    380   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    381 
    382 * ``allow_future``: A boolean specifying whether to include "future"
    383   objects on this page, where "future" means objects in which the field
    384   specified in ``date_field`` is greater than the current date/time. By
    385   default, this is ``False``.
    386 
    387 **Template name:**
    388 
    389 If ``template_name`` isn't specified, this view will use the template
    390 ``<app_label>/<model_name>_archive_month.html`` by default.
    391 
    392 **Template context:**
    393 
    394 .. versionadded:: 1.2
    395    The inclusion of ``date_list`` in the template's context is new.
    396 
    397 In addition to ``extra_context``, the template's context will be:
    398 
    399 * ``date_list``: A ``DateQuerySet`` object containing all days that have
    400   have objects available in the given month, according to ``queryset``,
    401   represented as ``datetime.datetime`` objects, in ascending order.
    402 
    403 * ``month``: A ``datetime.date`` object representing the given month.
    404 
    405 * ``next_month``: A ``datetime.date`` object representing the first day of
    406   the next month. If the next month is in the future, this will be
    407   ``None``.
    408 
    409 * ``previous_month``: A ``datetime.date`` object representing the first day
    410   of the previous month. Unlike ``next_month``, this will never be
    411   ``None``.
    412 
    413 * ``object_list``: A list of objects available for the given month. This
    414   variable's name depends on the ``template_object_name`` parameter, which
    415   is ``'object'`` by default. If ``template_object_name`` is ``'foo'``,
    416   this variable's name will be ``foo_list``.
    417 
    418 ``django.views.generic.date_based.archive_week``
    419 ------------------------------------------------
    420 
    421 **Description:**
    422 
    423 A weekly archive page showing all objects in a given week. Objects with a date
    424 in the *future* are not displayed unless you set ``allow_future`` to ``True``.
    425 
    426 **Required arguments:**
    427 
    428 * ``year``: The four-digit year for which the archive serves (a string).
    429 
    430 * ``week``: The week of the year for which the archive serves (a string).
    431   Weeks start with Sunday.
    432 
    433 * ``queryset``: A ``QuerySet`` of objects for which the archive serves.
    434 
    435 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    436   the ``QuerySet``'s model that the date-based archive should use to
    437   determine the objects on the page.
    438 
    439 **Optional arguments:**
    440 
    441 * ``template_name``: The full name of a template to use in rendering the
    442   page. This lets you override the default template name (see below).
    443 
    444 * ``template_loader``: The template loader to use when loading the
    445   template. By default, it's ``django.template.loader``.
    446 
    447 * ``extra_context``: A dictionary of values to add to the template
    448   context. By default, this is an empty dictionary. If a value in the
    449   dictionary is callable, the generic view will call it
    450   just before rendering the template.
    451 
    452 * ``allow_empty``: A boolean specifying whether to display the page if no
    453   objects are available. If this is ``False`` and no objects are available,
    454   the view will raise a 404 instead of displaying an empty page. By
    455   default, this is ``True``.
    456 
    457 * ``context_processors``: A list of template-context processors to apply to
    458   the view's template.
    459 
    460 * ``template_object_name``:  Designates the name of the template variable
    461   to use in the template context. By default, this is ``'object'``. The
    462   view will append ``'_list'`` to the value of this parameter in
    463   determining the variable's name.
    464 
    465 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    466   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    467 
    468 * ``allow_future``: A boolean specifying whether to include "future"
    469   objects on this page, where "future" means objects in which the field
    470   specified in ``date_field`` is greater than the current date/time. By
    471   default, this is ``False``.
    472 
    473 **Template name:**
    474 
    475 If ``template_name`` isn't specified, this view will use the template
    476 ``<app_label>/<model_name>_archive_week.html`` by default.
    477 
    478 **Template context:**
    479 
    480 In addition to ``extra_context``, the template's context will be:
    481 
    482 * ``week``: A ``datetime.date`` object representing the first day of the
    483   given week.
    484 
    485 * ``object_list``: A list of objects available for the given week. This
    486   variable's name depends on the ``template_object_name`` parameter, which
    487   is ``'object'`` by default. If ``template_object_name`` is ``'foo'``,
    488   this variable's name will be ``foo_list``.
    489 
    490 ``django.views.generic.date_based.archive_day``
    491 -----------------------------------------------
    492 
    493 **Description:**
    494 
    495 A day archive page showing all objects in a given day. Days in the future throw
    496 a 404 error, regardless of whether any objects exist for future days, unless
    497 you set ``allow_future`` to ``True``.
    498 
    499 **Required arguments:**
    500 
    501 * ``year``: The four-digit year for which the archive serves (a string).
    502 
    503 * ``month``: The month for which the archive serves, formatted according to
    504   the ``month_format`` argument.
    505 
    506 * ``day``: The day for which the archive serves, formatted according to the
    507   ``day_format`` argument.
    508 
    509 * ``queryset``: A ``QuerySet`` of objects for which the archive serves.
    510 
    511 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    512   the ``QuerySet``'s model that the date-based archive should use to
    513   determine the objects on the page.
    514 
    515 **Optional arguments:**
    516 
    517 * ``month_format``: A format string that regulates what format the ``month``
    518   parameter uses. This should be in the syntax accepted by Python's
    519   :func:`~time.strftime`. It's set to ``"%b"`` by default, which is a
    520   three-letter month abbreviation. To change it to use numbers, use
    521   ``"%m"``.
    522 
    523 * ``day_format``: Like ``month_format``, but for the ``day`` parameter.
    524   It defaults to ``"%d"`` (day of the month as a decimal number, 01-31).
    525 
    526 * ``template_name``: The full name of a template to use in rendering the
    527   page. This lets you override the default template name (see below).
    528 
    529 * ``template_loader``: The template loader to use when loading the
    530   template. By default, it's ``django.template.loader``.
    531 
    532 * ``extra_context``: A dictionary of values to add to the template
    533   context. By default, this is an empty dictionary. If a value in the
    534   dictionary is callable, the generic view will call it
    535   just before rendering the template.
    536 
    537 * ``allow_empty``: A boolean specifying whether to display the page if no
    538   objects are available. If this is ``False`` and no objects are available,
    539   the view will raise a 404 instead of displaying an empty page. By
    540   default, this is ``False``.
    541 
    542 * ``context_processors``: A list of template-context processors to apply to
    543   the view's template.
    544 
    545 * ``template_object_name``:  Designates the name of the template variable
    546   to use in the template context. By default, this is ``'object'``. The
    547   view will append ``'_list'`` to the value of this parameter in
    548   determining the variable's name.
    549 
    550 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    551   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    552 
    553 * ``allow_future``: A boolean specifying whether to include "future"
    554   objects on this page, where "future" means objects in which the field
    555   specified in ``date_field`` is greater than the current date/time. By
    556   default, this is ``False``.
    557 
    558 **Template name:**
    559 
    560 If ``template_name`` isn't specified, this view will use the template
    561 ``<app_label>/<model_name>_archive_day.html`` by default.
    562 
    563 **Template context:**
    564 
    565 In addition to ``extra_context``, the template's context will be:
    566 
    567 * ``day``: A ``datetime.date`` object representing the given day.
    568 
    569 * ``next_day``: A ``datetime.date`` object representing the next day. If
    570   the next day is in the future, this will be ``None``.
    571 
    572 * ``previous_day``: A ``datetime.date`` object representing the previous day.
    573   Unlike ``next_day``, this will never be ``None``.
    574 
    575 * ``object_list``: A list of objects available for the given day. This
    576   variable's name depends on the ``template_object_name`` parameter, which
    577   is ``'object'`` by default. If ``template_object_name`` is ``'foo'``,
    578   this variable's name will be ``foo_list``.
    579 
    580 ``django.views.generic.date_based.archive_today``
    581 -------------------------------------------------
    582 
    583 **Description:**
    584 
    585 A day archive page showing all objects for *today*. This is exactly the same as
    586 ``archive_day``, except the ``year``/``month``/``day`` arguments are not used,
    587 and today's date is used instead.
    588 
    589 ``django.views.generic.date_based.object_detail``
    590 -------------------------------------------------
    591 
    592 **Description:**
    593 
    594 A page representing an individual object. If the object has a date value in the
    595 future, the view will throw a 404 error by default, unless you set
    596 ``allow_future`` to ``True``.
    597 
    598 **Required arguments:**
    599 
    600 * ``year``: The object's four-digit year (a string).
    601 
    602 * ``month``: The object's month , formatted according to the
    603   ``month_format`` argument.
    604 
    605 * ``day``: The object's day , formatted according to the ``day_format``
    606   argument.
    607 
    608 * ``queryset``: A ``QuerySet`` that contains the object.
    609 
    610 * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in
    611   the ``QuerySet``'s model that the generic view should use to look up the
    612   object according to ``year``, ``month`` and ``day``.
    613 
    614 * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required.
    615 
    616   If you provide ``object_id``, it should be the value of the primary-key
    617   field for the object being displayed on this page.
    618 
    619   Otherwise, ``slug`` should be the slug of the given object, and
    620   ``slug_field`` should be the name of the slug field in the ``QuerySet``'s
    621   model. By default, ``slug_field`` is ``'slug'``.
    622 
    623 **Optional arguments:**
    624 
    625 * ``month_format``: A format string that regulates what format the ``month``
    626   parameter uses. This should be in the syntax accepted by Python's
    627   :func:`~time.strftime`. It's set to ``"%b"`` by default, which is a
    628   three-letter month abbreviation. To change it to use numbers, use
    629   ``"%m"``.
    630 
    631 * ``day_format``: Like ``month_format``, but for the ``day`` parameter.
    632   It defaults to ``"%d"`` (day of the month as a decimal number, 01-31).
    633 
    634 * ``template_name``: The full name of a template to use in rendering the
    635   page. This lets you override the default template name (see below).
    636 
    637 * ``template_name_field``: The name of a field on the object whose value is
    638   the template name to use. This lets you store template names in the data.
    639   In other words, if your object has a field ``'the_template'`` that
    640   contains a string ``'foo.html'``, and you set ``template_name_field`` to
    641   ``'the_template'``, then the generic view for this object will use the
    642   template ``'foo.html'``.
    643 
    644   It's a bit of a brain-bender, but it's useful in some cases.
    645 
    646 * ``template_loader``: The template loader to use when loading the
    647   template. By default, it's ``django.template.loader``.
    648 
    649 * ``extra_context``: A dictionary of values to add to the template
    650   context. By default, this is an empty dictionary. If a value in the
    651   dictionary is callable, the generic view will call it
    652   just before rendering the template.
    653 
    654 * ``context_processors``: A list of template-context processors to apply to
    655   the view's template.
    656 
    657 * ``template_object_name``:  Designates the name of the template variable
    658   to use in the template context. By default, this is ``'object'``.
    659 
    660 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    661   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    662 
    663 * ``allow_future``: A boolean specifying whether to include "future"
    664   objects on this page, where "future" means objects in which the field
    665   specified in ``date_field`` is greater than the current date/time. By
    666   default, this is ``False``.
    667 
    668 **Template name:**
    669 
    670 If ``template_name`` isn't specified, this view will use the template
    671 ``<app_label>/<model_name>_detail.html`` by default.
    672 
    673 **Template context:**
    674 
    675 In addition to ``extra_context``, the template's context will be:
    676 
    677 * ``object``: The object. This variable's name depends on the
    678   ``template_object_name`` parameter, which is ``'object'`` by default. If
    679   ``template_object_name`` is ``'foo'``, this variable's name will be
    680   ``foo``.
    681 
    682 .. module:: django.views.generic.list_detail
    683 
    684 List/detail generic views
    685 =========================
    686 
    687 The list-detail generic-view framework (in the
    688 ``django.views.generic.list_detail`` module) is similar to the date-based one,
    689 except the former simply has two views: a list of objects and an individual
    690 object page.
    691 
    692 ``django.views.generic.list_detail.object_list``
    693 ------------------------------------------------
    694 
    695 **Description:**
    696 
    697 A page representing a list of objects.
    698 
    699 **Required arguments:**
    700 
    701 * ``queryset``: A ``QuerySet`` that represents the objects.
    702 
    703 **Optional arguments:**
    704 
    705 * ``paginate_by``: An integer specifying how many objects should be
    706   displayed per page. If this is given, the view will paginate objects with
    707   ``paginate_by`` objects per page. The view will expect either a ``page``
    708   query string parameter (via ``GET``) or a ``page`` variable specified in
    709   the URLconf. See `Notes on pagination`_ below.
    710 
    711 * ``page``: The current page number, as an integer, or the string
    712   ``'last'``. This is 1-based. See `Notes on pagination`_ below.
    713 
    714 * ``template_name``: The full name of a template to use in rendering the
    715   page. This lets you override the default template name (see below).
    716 
    717 * ``template_loader``: The template loader to use when loading the
    718   template. By default, it's ``django.template.loader``.
    719 
    720 * ``extra_context``: A dictionary of values to add to the template
    721   context. By default, this is an empty dictionary. If a value in the
    722   dictionary is callable, the generic view will call it
    723   just before rendering the template.
    724 
    725 * ``allow_empty``: A boolean specifying whether to display the page if no
    726   objects are available. If this is ``False`` and no objects are available,
    727   the view will raise a 404 instead of displaying an empty page. By
    728   default, this is ``True``.
    729 
    730 * ``context_processors``: A list of template-context processors to apply to
    731   the view's template.
    732 
    733 * ``template_object_name``:  Designates the name of the template variable
    734   to use in the template context. By default, this is ``'object'``. The
    735   view will append ``'_list'`` to the value of this parameter in
    736   determining the variable's name.
    737 
    738 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    739   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    740 
    741 **Template name:**
    742 
    743 If ``template_name`` isn't specified, this view will use the template
    744 ``<app_label>/<model_name>_list.html`` by default.
    745 
    746 **Template context:**
    747 
    748 In addition to ``extra_context``, the template's context will be:
    749 
    750 * ``object_list``: The list of objects. This variable's name depends on the
    751   ``template_object_name`` parameter, which is ``'object'`` by default. If
    752   ``template_object_name`` is ``'foo'``, this variable's name will be
    753   ``foo_list``.
    754 
    755 * ``is_paginated``: A boolean representing whether the results are
    756   paginated. Specifically, this is set to ``False`` if the number of
    757   available objects is less than or equal to ``paginate_by``.
    758 
    759 If the results are paginated, the context will contain these extra variables:
    760 
    761 * ``paginator``: An instance of ``django.core.paginator.Paginator``.
    762 
    763 * ``page_obj``: An instance of ``django.core.paginator.Page``.
    764 
    765 Notes on pagination
    766 ~~~~~~~~~~~~~~~~~~~
    767 
    768 If ``paginate_by`` is specified, Django will paginate the results. You can
    769 specify the page number in the URL in one of two ways:
    770 
    771 * Use the ``page`` parameter in the URLconf. For example, this is what
    772   your URLconf might look like::
    773 
    774     (r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict))
    775 
    776 * Pass the page number via the ``page`` query-string parameter. For
    777   example, a URL would look like this::
    778 
    779     /objects/?page=3
    780 
    781 * To loop over all the available page numbers, use the ``page_range``
    782   variable. You can iterate over the list provided by ``page_range``
    783   to create a link to every page of results.
    784 
    785 These values and lists are 1-based, not 0-based, so the first page would be
    786 represented as page ``1``.
    787 
    788 For more on pagination, read the :doc:`pagination documentation
    789 </topics/pagination>`.
    790 
    791 As a special case, you are also permitted to use ``last`` as a value for
    792 ``page``::
    793 
    794     /objects/?page=last
    795 
    796 This allows you to access the final page of results without first having to
    797 determine how many pages there are.
    798 
    799 Note that ``page`` *must* be either a valid page number or the value ``last``;
    800 any other value for ``page`` will result in a 404 error.
    801 
    802 ``django.views.generic.list_detail.object_detail``
    803 --------------------------------------------------
    804 
    805 A page representing an individual object.
    806 
    807 **Description:**
    808 
    809 A page representing an individual object.
    810 
    811 **Required arguments:**
    812 
    813 * ``queryset``: A ``QuerySet`` that contains the object.
    814 
    815 * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required.
    816 
    817   If you provide ``object_id``, it should be the value of the primary-key
    818   field for the object being displayed on this page.
    819 
    820   Otherwise, ``slug`` should be the slug of the given object, and
    821   ``slug_field`` should be the name of the slug field in the ``QuerySet``'s
    822   model. By default, ``slug_field`` is ``'slug'``.
    823 
    824 **Optional arguments:**
    825 
    826 * ``template_name``: The full name of a template to use in rendering the
    827   page. This lets you override the default template name (see below).
    828 
    829 * ``template_name_field``: The name of a field on the object whose value is
    830   the template name to use. This lets you store template names in the data.
    831   In other words, if your object has a field ``'the_template'`` that
    832   contains a string ``'foo.html'``, and you set ``template_name_field`` to
    833   ``'the_template'``, then the generic view for this object will use the
    834   template ``'foo.html'``.
    835 
    836   It's a bit of a brain-bender, but it's useful in some cases.
    837 
    838 * ``template_loader``: The template loader to use when loading the
    839   template. By default, it's ``django.template.loader``.
    840 
    841 * ``extra_context``: A dictionary of values to add to the template
    842   context. By default, this is an empty dictionary. If a value in the
    843   dictionary is callable, the generic view will call it
    844   just before rendering the template.
    845 
    846 * ``context_processors``: A list of template-context processors to apply to
    847   the view's template.
    848 
    849 * ``template_object_name``:  Designates the name of the template variable
    850   to use in the template context. By default, this is ``'object'``.
    851 
    852 * ``mimetype``: The MIME type to use for the resulting document. Defaults
    853   to the value of the :setting:`DEFAULT_CONTENT_TYPE` setting.
    854 
    855 **Template name:**
    856 
    857 If ``template_name`` isn't specified, this view will use the template
    858 ``<app_label>/<model_name>_detail.html`` by default.
    859 
    860 **Template context:**
    861 
    862 In addition to ``extra_context``, the template's context will be:
    863 
    864 * ``object``: The object. This variable's name depends on the
    865   ``template_object_name`` parameter, which is ``'object'`` by default. If
    866   ``template_object_name`` is ``'foo'``, this variable's name will be
    867   ``foo``.
    868 
    869 .. module:: django.views.generic.create_update
    870 
    871 Create/update/delete generic views
    872 ==================================
    873 
    874 The ``django.views.generic.create_update`` module contains a set of functions
    875 for creating, editing and deleting objects.
    876 
    877 ``django.views.generic.create_update.create_object``
    878 ----------------------------------------------------
    879 
    880 **Description:**
    881 
    882 A page that displays a form for creating an object, redisplaying the form with
    883 validation errors (if there are any) and saving the object.
    884 
    885 **Required arguments:**
    886 
    887 * Either ``form_class`` or ``model`` is required.
    888 
    889   If you provide ``form_class``, it should be a ``django.forms.ModelForm``
    890   subclass. Use this argument when you need to customize the model's form.
    891   See the :doc:`ModelForm docs </topics/forms/modelforms>` for more
    892   information.
    893 
    894   Otherwise, ``model`` should be a Django model class and the form used
    895   will be a standard ``ModelForm`` for ``model``.
    896 
    897 **Optional arguments:**
    898 
    899 * ``post_save_redirect``: A URL to which the view will redirect after
    900   saving the object. By default, it's ``object.get_absolute_url()``.
    901 
    902   ``post_save_redirect`` may contain dictionary string formatting, which
    903   will be interpolated against the object's field attributes. For example,
    904   you could use ``post_save_redirect="/polls/%(slug)s/"``.
    905 
    906 * ``login_required``: A boolean that designates whether a user must be
    907   logged in, in order to see the page and save changes. This hooks into the
    908   Django :doc:`authentication system </topics/auth>`. By default, this is
    909   ``False``.
    910 
    911   If this is ``True``, and a non-logged-in user attempts to visit this page
    912   or save the form, Django will redirect the request to ``/accounts/login/``.
    913 
    914 * ``template_name``: The full name of a template to use in rendering the
    915   page. This lets you override the default template name (see below).
    916 
    917 * ``template_loader``: The template loader to use when loading the
    918   template. By default, it's ``django.template.loader``.
    919 
    920 * ``extra_context``: A dictionary of values to add to the template
    921   context. By default, this is an empty dictionary. If a value in the
    922   dictionary is callable, the generic view will call it
    923   just before rendering the template.
    924 
    925 * ``context_processors``: A list of template-context processors to apply to
    926   the view's template.
    927 
    928 **Template name:**
    929 
    930 If ``template_name`` isn't specified, this view will use the template
    931 ``<app_label>/<model_name>_form.html`` by default.
    932 
    933 **Template context:**
    934 
    935 In addition to ``extra_context``, the template's context will be:
    936 
    937 * ``form``: A ``django.forms.ModelForm`` instance representing the form
    938   for creating the object. This lets you refer to form fields easily in the
    939   template system.
    940 
    941   For example, if the model has two fields, ``name`` and ``address``::
    942 
    943       <form action="" method="post">
    944       <p>{{ form.name.label_tag }} {{ form.name }}</p>
    945       <p>{{ form.address.label_tag }} {{ form.address }}</p>
    946       </form>
    947 
    948   See the :doc:`forms documentation </topics/forms/index>` for more
    949   information about using ``Form`` objects in templates.
    950 
    951 ``django.views.generic.create_update.update_object``
    952 ----------------------------------------------------
    953 
    954 **Description:**
    955 
    956 A page that displays a form for editing an existing object, redisplaying the
    957 form with validation errors (if there are any) and saving changes to the
    958 object. This uses a form automatically generated from the object's
    959 model class.
    960 
    961 **Required arguments:**
    962 
    963 * Either ``form_class`` or ``model`` is required.
    964 
    965   If you provide ``form_class``, it should be a ``django.forms.ModelForm``
    966   subclass. Use this argument when you need to customize the model's form.
    967   See the :doc:`ModelForm docs </topics/forms/modelforms>` for more
    968   information.
    969 
    970   Otherwise, ``model`` should be a Django model class and the form used
    971   will be a standard ``ModelForm`` for ``model``.
    972 
    973 * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required.
    974 
    975   If you provide ``object_id``, it should be the value of the primary-key
    976   field for the object being displayed on this page.
    977 
    978   Otherwise, ``slug`` should be the slug of the given object, and
    979   ``slug_field`` should be the name of the slug field in the ``QuerySet``'s
    980   model. By default, ``slug_field`` is ``'slug'``.
    981 
    982 **Optional arguments:**
    983 
    984 * ``post_save_redirect``: A URL to which the view will redirect after
    985   saving the object. By default, it's ``object.get_absolute_url()``.
    986 
    987   ``post_save_redirect`` may contain dictionary string formatting, which
    988   will be interpolated against the object's field attributes. For example,
    989   you could use ``post_save_redirect="/polls/%(slug)s/"``.
    990 
    991 * ``login_required``: A boolean that designates whether a user must be
    992   logged in, in order to see the page and save changes. This hooks into the
    993   Django :doc:`authentication system </topics/auth>`. By default, this is
    994   ``False``.
    995 
    996   If this is ``True``, and a non-logged-in user attempts to visit this page
    997   or save the form, Django will redirect to :setting:`LOGIN_URL` (which
    998   defaults to ``/accounts/login/``).
    999 
    1000 * ``template_name``: The full name of a template to use in rendering the
    1001   page. This lets you override the default template name (see below).
    1002 
    1003 * ``template_loader``: The template loader to use when loading the
    1004   template. By default, it's ``django.template.loader``.
    1005 
    1006 * ``extra_context``: A dictionary of values to add to the template
    1007   context. By default, this is an empty dictionary. If a value in the
    1008   dictionary is callable, the generic view will call it
    1009   just before rendering the template.
    1010 
    1011 * ``context_processors``: A list of template-context processors to apply to
    1012   the view's template.
    1013 
    1014 * ``template_object_name``:  Designates the name of the template variable
    1015   to use in the template context. By default, this is ``'object'``.
    1016 
    1017 **Template name:**
    1018 
    1019 If ``template_name`` isn't specified, this view will use the template
    1020 ``<app_label>/<model_name>_form.html`` by default.
    1021 
    1022 **Template context:**
    1023 
    1024 In addition to ``extra_context``, the template's context will be:
    1025 
    1026 * ``form``: A ``django.forms.ModelForm`` instance representing the form
    1027   for editing the object. This lets you refer to form fields easily in the
    1028   template system.
    1029 
    1030   For example, if the model has two fields, ``name`` and ``address``::
    1031 
    1032       <form action="" method="post">
    1033       <p>{{ form.name.label_tag }} {{ form.name }}</p>
    1034       <p>{{ form.address.label_tag }} {{ form.address }}</p>
    1035       </form>
    1036 
    1037   See the :doc:`forms documentation </topics/forms/index>` for more
    1038   information about using ``Form`` objects in templates.
    1039 
    1040 * ``object``: The original object being edited. This variable's name
    1041   depends on the ``template_object_name`` parameter, which is ``'object'``
    1042   by default. If ``template_object_name`` is ``'foo'``, this variable's
    1043   name will be ``foo``.
    1044 
    1045 ``django.views.generic.create_update.delete_object``
    1046 ----------------------------------------------------
    1047 
    1048 **Description:**
    1049 
    1050 A view that displays a confirmation page and deletes an existing object. The
    1051 given object will only be deleted if the request method is ``POST``. If this
    1052 view is fetched via ``GET``, it will display a confirmation page that should
    1053 contain a form that POSTs to the same URL.
    1054 
    1055 **Required arguments:**
    1056 
    1057 * ``model``: The Django model class of the object that the form will
    1058   delete.
    1059 
    1060 * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required.
    1061 
    1062   If you provide ``object_id``, it should be the value of the primary-key
    1063   field for the object being displayed on this page.
    1064 
    1065   Otherwise, ``slug`` should be the slug of the given object, and
    1066   ``slug_field`` should be the name of the slug field in the ``QuerySet``'s
    1067   model. By default, ``slug_field`` is ``'slug'``.
    1068 
    1069 * ``post_delete_redirect``: A URL to which the view will redirect after
    1070   deleting the object.
    1071 
    1072 **Optional arguments:**
    1073 
    1074 * ``login_required``: A boolean that designates whether a user must be
    1075   logged in, in order to see the page and save changes. This hooks into the
    1076   Django :doc:`authentication system </topics/auth>`. By default, this is
    1077   ``False``.
    1078 
    1079   If this is ``True``, and a non-logged-in user attempts to visit this page
    1080   or save the form, Django will redirect to :setting:`LOGIN_URL` (which
    1081   defaults to ``/accounts/login/``).
    1082 
    1083 * ``template_name``: The full name of a template to use in rendering the
    1084   page. This lets you override the default template name (see below).
    1085 
    1086 * ``template_loader``: The template loader to use when loading the
    1087   template. By default, it's ``django.template.loader``.
    1088 
    1089 * ``extra_context``: A dictionary of values to add to the template
    1090   context. By default, this is an empty dictionary. If a value in the
    1091   dictionary is callable, the generic view will call it
    1092   just before rendering the template.
    1093 
    1094 * ``context_processors``: A list of template-context processors to apply to
    1095   the view's template.
    1096 
    1097 * ``template_object_name``:  Designates the name of the template variable
    1098   to use in the template context. By default, this is ``'object'``.
    1099 
    1100 **Template name:**
    1101 
    1102 If ``template_name`` isn't specified, this view will use the template
    1103 ``<app_label>/<model_name>_confirm_delete.html`` by default.
    1104 
    1105 **Template context:**
    1106 
    1107 In addition to ``extra_context``, the template's context will be:
    1108 
    1109 * ``object``: The original object that's about to be deleted. This
    1110   variable's name depends on the ``template_object_name`` parameter, which
    1111   is ``'object'`` by default. If ``template_object_name`` is ``'foo'``,
    1112   this variable's name will be ``foo``.
  • docs/ref/index.txt

    diff --git a/docs/ref/index.txt b/docs/ref/index.txt
    index db09afe..32b2631 100644
    a b API Reference  
    2424   unicode
    2525   utils
    2626   validators
    27 
    28 Deprecated features
    29 -------------------
    30 
    31 .. toctree::
    32    :maxdepth: 1
    33 
    34    generic-views
  • docs/releases/1.1-alpha-1.txt

    diff --git a/docs/releases/1.1-alpha-1.txt b/docs/releases/1.1-alpha-1.txt
    index b123a06..10b0d5d 100644
    a b Other new features and changes introduced since Django 1.0 include:  
    8888  which return the list of hidden -- i.e., ``<input type="hidden">`` -- and
    8989  visible fields on the form, respectively.
    9090
    91 * The ``redirect_to`` generic view (see :doc:`the generic views documentation
    92   </ref/generic-views>`) now accepts an additional keyword argument
     91* The ``redirect_to`` generic view
     92  now accepts an additional keyword argument
    9393  ``permanent``. If ``permanent`` is ``True``, the view will emit an HTTP
    9494  permanent redirect (status code 301). If ``False``, the view will emit an HTTP
    9595  temporary redirect (status code 302).
  • docs/releases/1.1.txt

    diff --git a/docs/releases/1.1.txt b/docs/releases/1.1.txt
    index 15e9c84..1294011 100644
    a b Other new features and changes introduced since Django 1.0 include:  
    396396  which return the list of hidden -- i.e., ``<input type="hidden">`` -- and
    397397  visible fields on the form, respectively.
    398398
    399 * The ``redirect_to`` generic view (see :doc:`the generic views documentation
    400   </ref/generic-views>`) now accepts an additional keyword argument
     399* The ``redirect_to`` generic view
     400  now accepts an additional keyword argument
    401401  ``permanent``. If ``permanent`` is ``True``, the view will emit an HTTP
    402402  permanent redirect (status code 301). If ``False``, the view will emit an HTTP
    403403  temporary redirect (status code 302).
  • docs/releases/1.3-alpha-1.txt

    diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt
    index f1ff23b..4e6c72a 100644
    a b the basis for reusable applications that can be easily extended.  
    4141See :doc:`the documentation on Class-based Generic Views
    4242</topics/class-based-views>` for more details. There is also a document to
    4343help you :doc:`convert your function-based generic views to class-based
    44 views</topics/generic-views-migration>`.
     44views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.
    4545
    4646Logging
    4747~~~~~~~
  • docs/releases/1.3.txt

    diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt
    index 224cd47..3548a00 100644
    a b used as the basis for reusable applications that can be easily  
    8181extended.
    8282
    8383See :doc:`the documentation on class-based generic views</topics/class-based-views>`
    84 for more details. There is also a document to help you :doc:`convert
     84for more details. There is also a document to help you `convert
    8585your function-based generic views to class-based
    86 views</topics/generic-views-migration>`.
     86views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.
    8787
    8888Logging
    8989~~~~~~~
  • docs/topics/auth.txt

    diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
    index d266b92..b3ed834 100644
    a b To limit access to a :doc:`class-based generic view </ref/class-based-views>`,  
    14721472decorate the :meth:`View.dispatch <django.views.generic.base.View.dispatch>`
    14731473method on the class. See :ref:`decorating-class-based-views` for details.
    14741474
    1475 Function-based generic views
    1476 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1477 
    1478 To limit access to a :doc:`function-based generic view </ref/generic-views>`,
    1479 write a thin wrapper around the view, and point your URLconf to your wrapper
    1480 instead of the generic view itself. For example::
    1481 
    1482     from django.views.generic.date_based import object_detail
    1483 
    1484     @login_required
    1485     def limited_object_detail(*args, **kwargs):
    1486         return object_detail(*args, **kwargs)
    1487 
    14881475.. _permissions:
    14891476
    14901477Permissions
  • docs/topics/class-based-views.txt

    diff --git a/docs/topics/class-based-views.txt b/docs/topics/class-based-views.txt
    index e1e2618..6df8f78 100644
    a b Class-based generic views  
    66
    77.. note::
    88    Prior to Django 1.3, generic views were implemented as functions. The
    9     function-based implementation has been deprecated in favor of the
     9    function-based implementation has been removed in favor of the
    1010    class-based approach described here.
    1111
    12     For details on the previous generic views implementation,
    13     see the :doc:`topic guide </topics/generic-views>` and
    14     :doc:`detailed reference </ref/generic-views>`.
    15 
    1612Writing Web applications can be monotonous, because we repeat certain patterns
    1713again and again. Django tries to take away some of that monotony at the model
    1814and template layers, but Web developers also experience this boredom at the view
  • deleted file docs/topics/generic-views-migration.txt

    diff --git a/docs/topics/generic-views-migration.txt b/docs/topics/generic-views-migration.txt
    deleted file mode 100644
    index 0647336..0000000
    + -  
    1 ======================================
    2 Migrating function-based generic views
    3 ======================================
    4 
    5 All the :doc:`function-based generic views</ref/generic-views>`
    6 that existed in Django 1.2 have analogs as :doc:`class-based generic
    7 views</ref/class-based-views>` in Django 1.3. The feature set
    8 exposed in those function-based views can be replicated in a
    9 class-based way.
    10 
    11 How to migrate
    12 ==============
    13 
    14 Replace generic views with generic classes
    15 ------------------------------------------
    16 
    17 Existing usage of function-based generic views should be replaced with
    18 their class-based analogs:
    19 
    20 ====================================================  ====================================================
    21 Old function-based generic view                       New class-based generic view
    22 ====================================================  ====================================================
    23 ``django.views.generic.simple.direct_to_template``    :class:`django.views.generic.base.TemplateView`
    24 ``django.views.generic.simple.redirect_to``           :class:`django.views.generic.base.RedirectView`
    25 ``django.views.generic.list_detail.object_list``      :class:`django.views.generic.list.ListView`
    26 ``django.views.generic.list_detail.object_detail``    :class:`django.views.generic.detail.DetailView`
    27 ``django.views.generic.create_update.create_object``  :class:`django.views.generic.edit.CreateView`
    28 ``django.views.generic.create_update.update_object``  :class:`django.views.generic.edit.UpdateView`
    29 ``django.views.generic.create_update.delete_object``  :class:`django.views.generic.edit.DeleteView`
    30 ``django.views.generic.date_based.archive_index``     :class:`django.views.generic.dates.ArchiveIndexView`
    31 ``django.views.generic.date_based.archive_year``      :class:`django.views.generic.dates.YearArchiveView`
    32 ``django.views.generic.date_based.archive_month``     :class:`django.views.generic.dates.MonthArchiveView`
    33 ``django.views.generic.date_based.archive_week``      :class:`django.views.generic.dates.WeekArchiveView`
    34 ``django.views.generic.date_based.archive_day``       :class:`django.views.generic.dates.DayArchiveView`
    35 ``django.views.generic.date_based.archive_today``     :class:`django.views.generic.dates.TodayArchiveView`
    36 ``django.views.generic.date_based.object_detail``     :class:`django.views.generic.dates.DateDetailView`
    37 ====================================================  ====================================================
    38 
    39 To do this, replace the reference to the generic view function with
    40 a ``as_view()`` instantiation of the class-based view. For example,
    41 the old-style ``direct_to_template`` pattern::
    42 
    43     ('^about/$', direct_to_template, {'template': 'about.html'})
    44 
    45 can be replaced with an instance of
    46 :class:`~django.views.generic.base.TemplateView`::
    47 
    48     ('^about/$', TemplateView.as_view(template_name='about.html'))
    49 
    50 ``template`` argument to ``direct_to_template`` views
    51 -----------------------------------------------------
    52 
    53 The ``template`` argument to the ``direct_to_template`` view has been renamed
    54 ``template_name``. This has been done to maintain consistency with other views.
    55 
    56 ``object_id`` argument to detail views
    57 --------------------------------------
    58 
    59 The object_id argument to the ``object_detail`` view has been renamed
    60 ``pk`` on the :class:`~django.views.generic.detail.DetailView`.
    61 
    62 ``template_object_name``
    63 ------------------------
    64 
    65 ``template_object_name`` has been renamed ``context_object_name``,
    66 reflecting the fact that the context data can be used for purposes
    67 other than template rendering (e.g., to populate JSON output).
    68 
    69 The ``_list`` suffix on list views
    70 ----------------------------------
    71 
    72 In a function-based :class:`ListView`, the ``template_object_name``
    73 was appended with the suffix ``'_list'`` to yield the final context
    74 variable name. In a class-based ``ListView``, the
    75 ``context_object_name`` is used verbatim. The ``'_list'`` suffix
    76 is only applied when generating a default context object name.
    77 
    78 The context data for ``object_list`` views
    79 ------------------------------------------
    80 
    81 The context provided by :class:`~django.views.generic.list.MultipleObjectMixin`
    82 is quite different from that provided by ``object_list``, with most pagination
    83 related variables replaced by a single ``page_obj`` object. The following are no
    84 longer provided:
    85 
    86 * ``first_on_page``
    87 * ``has_next``
    88 * ``has_previous``
    89 * ``hits``
    90 * ``last_on_page``
    91 * ``next``
    92 * ``page_range``
    93 * ``page``
    94 * ``pages``
    95 * ``previous``
    96 * ``results_per_page``
    97 
    98 ``extra_context``
    99 -----------------
    100 
    101 Function-based generic views provided an ``extra_context`` argument
    102 as way to insert extra items into the context at time of rendering.
    103 
    104 Class-based views don't provide an ``extra_context`` argument.
    105 Instead, you subclass the view, overriding :meth:`get_context_data()`.
    106 For example::
    107 
    108     class MyListView(ListView):
    109         def get_context_data(self, **kwargs):
    110             context = super(MyListView, self).get_context_data(**kwargs)
    111             context.update({
    112                 'foo': 42,
    113                 'bar': 37
    114             })
    115             return context
    116 
    117 ``post_save_redirect`` argument to create and update views
    118 ----------------------------------------------------------
    119 
    120 The ``post_save_redirect`` argument to the create and update views
    121 has been renamed ``success_url`` on the
    122 :class:`~django.views.generic.edit.ModelFormMixin`.
    123 
    124 ``mimetype``
    125 ------------
    126 
    127 Some function-based generic views provided a ``mimetype`` argument
    128 as way to control the mimetype of the response.
    129 
    130 Class-based views don't provide a ``mimetype`` argument. Instead, you
    131 subclass the view, overriding
    132 :meth:`TemplateResponseMixin.render_to_response()` and pass in arguments for
    133 the TemplateResponse constructor. For example::
    134 
    135     class MyListView(ListView):
    136         def render_to_response(self, context, **kwargs):
    137             return super(MyListView, self).render_to_response(context,
    138                             content_type='application/json', **kwargs)
    139 
    140 ``context_processors``
    141 ----------------------
    142 
    143 Some function-based generic views provided a ``context_processors``
    144 argument that could be used to force the use of specialized context
    145 processors when rendering template content.
    146 
    147 Class-based views don't provide a ``context_processors`` argument.
    148 Instead, you subclass the view, overriding
    149 :meth:`TemplateResponseMixin.render_to_response()`, and passing in
    150 a context instance that has been instantiated with the processors
    151 you want to use. For example::
    152 
    153     class MyListView(ListView):
    154         def render_to_response(self, context, **kwargs):
    155             return super(MyListView, self).render_to_response(
    156                     RequestContext(self.request,
    157                                    context,
    158                                    processors=[custom_processor]),
    159                     **kwargs)
  • deleted file docs/topics/generic-views.txt

    diff --git a/docs/topics/generic-views.txt b/docs/topics/generic-views.txt
    deleted file mode 100644
    index 77232bc..0000000
    + -  
    1 =============
    2 Generic views
    3 =============
    4 
    5 
    6 .. versionchanged:: 1.3
    7 
    8 .. note::
    9 
    10     From Django 1.3, function-based generic views have been deprecated in favor
    11     of a class-based approach, described in the class-based views :doc:`topic
    12     guide </topics/class-based-views>` and :doc:`detailed reference
    13     </ref/class-based-views>`.
    14 
    15 Writing Web applications can be monotonous, because we repeat certain patterns
    16 again and again. Django tries to take away some of that monotony at the model
    17 and template layers, but Web developers also experience this boredom at the view
    18 level.
    19 
    20 Django's *generic views* were developed to ease that pain. They take certain
    21 common idioms and patterns found in view development and abstract them so that
    22 you can quickly write common views of data without having to write too much
    23 code.
    24 
    25 We can recognize certain common tasks, like displaying a list of objects, and
    26 write code that displays a list of *any* object. Then the model in question can
    27 be passed as an extra argument to the URLconf.
    28 
    29 Django ships with generic views to do the following:
    30 
    31 * Perform common "simple" tasks: redirect to a different page and
    32   render a given template.
    33 
    34 * Display list and detail pages for a single object. If we were creating an
    35   application to manage conferences then a ``talk_list`` view and a
    36   ``registered_user_list`` view would be examples of list views. A single
    37   talk page is an example of what we call a "detail" view.
    38 
    39 * Present date-based objects in year/month/day archive pages,
    40   associated detail, and "latest" pages. The Django Weblog's
    41   (https://www.djangoproject.com/weblog/) year, month, and
    42   day archives are built with these, as would be a typical
    43   newspaper's archives.
    44 
    45 * Allow users to create, update, and delete objects -- with or
    46   without authorization.
    47 
    48 Taken together, these views provide easy interfaces to perform the most common
    49 tasks developers encounter.
    50 
    51 Using generic views
    52 ===================
    53 
    54 All of these views are used by creating configuration dictionaries in
    55 your URLconf files and passing those dictionaries as the third member of the
    56 URLconf tuple for a given pattern.
    57 
    58 For example, here's a simple URLconf you could use to present a static "about"
    59 page::
    60 
    61     from django.conf.urls import patterns, url, include
    62     from django.views.generic.simple import direct_to_template
    63 
    64     urlpatterns = patterns('',
    65         ('^about/$', direct_to_template, {
    66             'template': 'about.html'
    67         })
    68     )
    69 
    70 Though this might seem a bit "magical" at first glance  -- look, a view with no
    71 code! --, actually the ``direct_to_template`` view simply grabs information from
    72 the extra-parameters dictionary and uses that information when rendering the
    73 view.
    74 
    75 Because this generic view -- and all the others -- is a regular view function
    76 like any other, we can reuse it inside our own views. As an example, let's
    77 extend our "about" example to map URLs of the form ``/about/<whatever>/`` to
    78 statically rendered ``about/<whatever>.html``. We'll do this by first modifying
    79 the URLconf to point to a view function:
    80 
    81 .. parsed-literal::
    82 
    83     from django.conf.urls import patterns, url, include
    84     from django.views.generic.simple import direct_to_template
    85     **from books.views import about_pages**
    86 
    87     urlpatterns = patterns('',
    88         ('^about/$', direct_to_template, {
    89             'template': 'about.html'
    90         }),
    91         **('^about/(\\w+)/$', about_pages),**
    92     )
    93 
    94 Next, we'll write the ``about_pages`` view::
    95 
    96     from django.http import Http404
    97     from django.template import TemplateDoesNotExist
    98     from django.views.generic.simple import direct_to_template
    99 
    100     def about_pages(request, page):
    101         try:
    102             return direct_to_template(request, template="about/%s.html" % page)
    103         except TemplateDoesNotExist:
    104             raise Http404()
    105 
    106 Here we're treating ``direct_to_template`` like any other function. Since it
    107 returns an ``HttpResponse``, we can simply return it as-is. The only slightly
    108 tricky business here is dealing with missing templates. We don't want a
    109 nonexistent template to cause a server error, so we catch
    110 ``TemplateDoesNotExist`` exceptions and return 404 errors instead.
    111 
    112 .. admonition:: Is there a security vulnerability here?
    113 
    114     Sharp-eyed readers may have noticed a possible security hole: we're
    115     constructing the template name using interpolated content from the browser
    116     (``template="about/%s.html" % page``). At first glance, this looks like a
    117     classic *directory traversal* vulnerability. But is it really?
    118 
    119     Not exactly. Yes, a maliciously crafted value of ``page`` could cause
    120     directory traversal, but although ``page`` *is* taken from the request URL,
    121     not every value will be accepted. The key is in the URLconf: we're using
    122     the regular expression ``\w+`` to match the ``page`` part of the URL, and
    123     ``\w`` only accepts letters and numbers. Thus, any malicious characters
    124     (dots and slashes, here) will be rejected by the URL resolver before they
    125     reach the view itself.
    126 
    127 Generic views of objects
    128 ========================
    129 
    130 The ``direct_to_template`` certainly is useful, but Django's generic views
    131 really shine when it comes to presenting views on your database content. Because
    132 it's such a common task, Django comes with a handful of built-in generic views
    133 that make generating list and detail views of objects incredibly easy.
    134 
    135 Let's take a look at one of these generic views: the "object list" view. We'll
    136 be using these models::
    137 
    138     # models.py
    139     from django.db import models
    140 
    141     class Publisher(models.Model):
    142         name = models.CharField(max_length=30)
    143         address = models.CharField(max_length=50)
    144         city = models.CharField(max_length=60)
    145         state_province = models.CharField(max_length=30)
    146         country = models.CharField(max_length=50)
    147         website = models.URLField()
    148 
    149         def __unicode__(self):
    150             return self.name
    151 
    152         class Meta:
    153             ordering = ["-name"]
    154 
    155     class Book(models.Model):
    156         title = models.CharField(max_length=100)
    157         authors = models.ManyToManyField('Author')
    158         publisher = models.ForeignKey(Publisher)
    159         publication_date = models.DateField()
    160 
    161 To build a list page of all publishers, we'd use a URLconf along these lines::
    162 
    163     from django.conf.urls import patterns, url, include
    164     from django.views.generic import list_detail
    165     from books.models import Publisher
    166 
    167     publisher_info = {
    168         "queryset" : Publisher.objects.all(),
    169     }
    170 
    171     urlpatterns = patterns('',
    172         (r'^publishers/$', list_detail.object_list, publisher_info)
    173     )
    174 
    175 That's all the Python code we need to write. We still need to write a template,
    176 however. We could explicitly tell the ``object_list`` view which template to use
    177 by including a ``template_name`` key in the extra arguments dictionary, but in
    178 the absence of an explicit template Django will infer one from the object's
    179 name. In this case, the inferred template will be
    180 ``"books/publisher_list.html"`` -- the "books" part comes from the name of the
    181 app that defines the model, while the "publisher" bit is just the lowercased
    182 version of the model's name.
    183 
    184 .. highlightlang:: html+django
    185 
    186 This template will be rendered against a context containing a variable called
    187 ``object_list`` that contains all the publisher objects. A very simple template
    188 might look like the following::
    189 
    190     {% extends "base.html" %}
    191 
    192     {% block content %}
    193         <h2>Publishers</h2>
    194         <ul>
    195             {% for publisher in object_list %}
    196                 <li>{{ publisher.name }}</li>
    197             {% endfor %}
    198         </ul>
    199     {% endblock %}
    200 
    201 That's really all there is to it. All the cool features of generic views come
    202 from changing the "info" dictionary passed to the generic view. The
    203 :doc:`generic views reference</ref/generic-views>` documents all the generic
    204 views and all their options in detail; the rest of this document will consider
    205 some of the common ways you might customize and extend generic views.
    206 
    207 Extending generic views
    208 =======================
    209 
    210 .. highlightlang:: python
    211 
    212 There's no question that using generic views can speed up development
    213 substantially. In most projects, however, there comes a moment when the
    214 generic views no longer suffice. Indeed, the most common question asked by new
    215 Django developers is how to make generic views handle a wider array of
    216 situations.
    217 
    218 Luckily, in nearly every one of these cases, there are ways to simply extend
    219 generic views to handle a larger array of use cases. These situations usually
    220 fall into a handful of patterns dealt with in the sections that follow.
    221 
    222 Making "friendly" template contexts
    223 -----------------------------------
    224 
    225 You might have noticed that our sample publisher list template stores all the
    226 books in a variable named ``object_list``. While this works just fine, it isn't
    227 all that "friendly" to template authors: they have to "just know" that they're
    228 dealing with publishers here. A better name for that variable would be
    229 ``publisher_list``; that variable's content is pretty obvious.
    230 
    231 We can change the name of that variable easily with the ``template_object_name``
    232 argument:
    233 
    234 .. parsed-literal::
    235 
    236     publisher_info = {
    237         "queryset" : Publisher.objects.all(),
    238         **"template_object_name" : "publisher",**
    239     }
    240 
    241     urlpatterns = patterns('',
    242         (r'^publishers/$', list_detail.object_list, publisher_info)
    243     )
    244 
    245 Providing a useful ``template_object_name`` is always a good idea. Your
    246 coworkers who design templates will thank you.
    247 
    248 Adding extra context
    249 --------------------
    250 
    251 Often you simply need to present some extra information beyond that provided by
    252 the generic view. For example, think of showing a list of all the books on each
    253 publisher detail page. The ``object_detail`` generic view provides the
    254 publisher to the context, but it seems there's no way to get additional
    255 information in that template.
    256 
    257 But there is: all generic views take an extra optional parameter,
    258 ``extra_context``. This is a dictionary of extra objects that will be added to
    259 the template's context. So, to provide the list of all books on the detail
    260 detail view, we'd use an info dict like this:
    261 
    262 .. parsed-literal::
    263 
    264     from books.models import Publisher, **Book**
    265 
    266     publisher_info = {
    267         "queryset" : Publisher.objects.all(),
    268         "template_object_name" : "publisher",
    269         **"extra_context" : {"book_list" : Book.objects.all()}**
    270     }
    271 
    272 This would populate a ``{{ book_list }}`` variable in the template context.
    273 This pattern can be used to pass any information down into the template for the
    274 generic view. It's very handy.
    275 
    276 However, there's actually a subtle bug here -- can you spot it?
    277 
    278 The problem has to do with when the queries in ``extra_context`` are evaluated.
    279 Because this example puts ``Book.objects.all()`` in the URLconf, it will
    280 be evaluated only once (when the URLconf is first loaded). Once you add or
    281 remove books, you'll notice that the generic view doesn't reflect those
    282 changes until you reload the Web server (see :ref:`caching-and-querysets`
    283 for more information about when QuerySets are cached and evaluated).
    284 
    285 .. note::
    286 
    287     This problem doesn't apply to the ``queryset`` generic view argument. Since
    288     Django knows that particular QuerySet should *never* be cached, the generic
    289     view takes care of clearing the cache when each view is rendered.
    290 
    291 The solution is to use a callback in ``extra_context`` instead of a value. Any
    292 callable (i.e., a function) that's passed to ``extra_context`` will be evaluated
    293 when the view is rendered (instead of only once). You could do this with an
    294 explicitly defined function:
    295 
    296 .. parsed-literal::
    297 
    298     def get_books():
    299         return Book.objects.all()
    300 
    301     publisher_info = {
    302         "queryset" : Publisher.objects.all(),
    303         "template_object_name" : "publisher",
    304         "extra_context" : **{"book_list" : get_books}**
    305     }
    306 
    307 or you could use a less obvious but shorter version that relies on the fact that
    308 ``Book.objects.all`` is itself a callable:
    309 
    310 .. parsed-literal::
    311 
    312     publisher_info = {
    313         "queryset" : Publisher.objects.all(),
    314         "template_object_name" : "publisher",
    315         "extra_context" : **{"book_list" : Book.objects.all}**
    316     }
    317 
    318 Notice the lack of parentheses after ``Book.objects.all``; this references
    319 the function without actually calling it (which the generic view will do later).
    320 
    321 Viewing subsets of objects
    322 --------------------------
    323 
    324 Now let's take a closer look at this ``queryset`` key we've been using all
    325 along. Most generic views take one of these ``queryset`` arguments -- it's how
    326 the view knows which set of objects to display (see :doc:`/topics/db/queries` for
    327 more information about ``QuerySet`` objects, and see the
    328 :doc:`generic views reference</ref/generic-views>` for the complete details).
    329 
    330 To pick a simple example, we might want to order a list of books by
    331 publication date, with the most recent first:
    332 
    333 .. parsed-literal::
    334 
    335     book_info = {
    336         "queryset" : Book.objects.all().order_by("-publication_date"),
    337     }
    338 
    339     urlpatterns = patterns('',
    340         (r'^publishers/$', list_detail.object_list, publisher_info),
    341         **(r'^books/$', list_detail.object_list, book_info),**
    342     )
    343 
    344 
    345 That's a pretty simple example, but it illustrates the idea nicely. Of course,
    346 you'll usually want to do more than just reorder objects. If you want to
    347 present a list of books by a particular publisher, you can use the same
    348 technique:
    349 
    350 .. parsed-literal::
    351 
    352     **acme_books = {**
    353         **"queryset": Book.objects.filter(publisher__name="Acme Publishing"),**
    354         **"template_name" : "books/acme_list.html"**
    355     **}**
    356 
    357     urlpatterns = patterns('',
    358         (r'^publishers/$', list_detail.object_list, publisher_info),
    359         **(r'^books/acme/$', list_detail.object_list, acme_books),**
    360     )
    361 
    362 Notice that along with a filtered ``queryset``, we're also using a custom
    363 template name. If we didn't, the generic view would use the same template as the
    364 "vanilla" object list, which might not be what we want.
    365 
    366 Also notice that this isn't a very elegant way of doing publisher-specific
    367 books. If we want to add another publisher page, we'd need another handful of
    368 lines in the URLconf, and more than a few publishers would get unreasonable.
    369 We'll deal with this problem in the next section.
    370 
    371 .. note::
    372 
    373     If you get a 404 when requesting ``/books/acme/``, check to ensure you
    374     actually have a Publisher with the name 'ACME Publishing'.  Generic
    375     views have an ``allow_empty`` parameter for this case.  See the
    376     :doc:`generic views reference</ref/generic-views>` for more details.
    377 
    378 Complex filtering with wrapper functions
    379 ----------------------------------------
    380 
    381 Another common need is to filter down the objects given in a list page by some
    382 key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but
    383 what if we wanted to write a view that displayed all the books by some arbitrary
    384 publisher? We can "wrap" the ``object_list`` generic view to avoid writing a lot
    385 of code by hand. As usual, we'll start by writing a URLconf:
    386 
    387 .. parsed-literal::
    388 
    389     from books.views import books_by_publisher
    390 
    391     urlpatterns = patterns('',
    392         (r'^publishers/$', list_detail.object_list, publisher_info),
    393         **(r'^books/(\\w+)/$', books_by_publisher),**
    394     )
    395 
    396 Next, we'll write the ``books_by_publisher`` view itself::
    397 
    398     from django.http import Http404
    399     from django.views.generic import list_detail
    400     from books.models import Book, Publisher
    401 
    402     def books_by_publisher(request, name):
    403 
    404         # Look up the publisher (and raise a 404 if it can't be found).
    405         try:
    406             publisher = Publisher.objects.get(name__iexact=name)
    407         except Publisher.DoesNotExist:
    408             raise Http404
    409 
    410         # Use the object_list view for the heavy lifting.
    411         return list_detail.object_list(
    412             request,
    413             queryset = Book.objects.filter(publisher=publisher),
    414             template_name = "books/books_by_publisher.html",
    415             template_object_name = "books",
    416             extra_context = {"publisher" : publisher}
    417         )
    418 
    419 This works because there's really nothing special about generic views -- they're
    420 just Python functions. Like any view function, generic views expect a certain
    421 set of arguments and return ``HttpResponse`` objects. Thus, it's incredibly easy
    422 to wrap a small function around a generic view that does additional work before
    423 (or after; see the next section) handing things off to the generic view.
    424 
    425 .. note::
    426 
    427     Notice that in the preceding example we passed the current publisher being
    428     displayed in the ``extra_context``. This is usually a good idea in wrappers
    429     of this nature; it lets the template know which "parent" object is currently
    430     being browsed.
    431 
    432 Performing extra work
    433 ---------------------
    434 
    435 The last common pattern we'll look at involves doing some extra work before
    436 or after calling the generic view.
    437 
    438 Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
    439 using to keep track of the last time anybody looked at that author::
    440 
    441     # models.py
    442 
    443     class Author(models.Model):
    444         salutation = models.CharField(max_length=10)
    445         first_name = models.CharField(max_length=30)
    446         last_name = models.CharField(max_length=40)
    447         email = models.EmailField()
    448         headshot = models.ImageField(upload_to='/tmp')
    449         last_accessed = models.DateTimeField()
    450 
    451 The generic ``object_detail`` view, of course, wouldn't know anything about this
    452 field, but once again we could easily write a custom view to keep that field
    453 updated.
    454 
    455 First, we'd need to add an author detail bit in the URLconf to point to a
    456 custom view:
    457 
    458 .. parsed-literal::
    459 
    460     from books.views import author_detail
    461 
    462     urlpatterns = patterns('',
    463         #...
    464         **(r'^authors/(?P<author_id>\\d+)/$', author_detail),**
    465     )
    466 
    467 Then we'd write our wrapper function::
    468 
    469     import datetime
    470     from books.models import Author
    471     from django.views.generic import list_detail
    472     from django.shortcuts import get_object_or_404
    473 
    474     def author_detail(request, author_id):
    475         # Look up the Author (and raise a 404 if she's not found)
    476         author = get_object_or_404(Author, pk=author_id)
    477 
    478         # Record the last accessed date
    479         author.last_accessed = datetime.datetime.now()
    480         author.save()
    481 
    482         # Show the detail page
    483         return list_detail.object_detail(
    484             request,
    485             queryset = Author.objects.all(),
    486             object_id = author_id,
    487         )
    488 
    489 .. note::
    490 
    491     This code won't actually work unless you create a
    492     ``books/author_detail.html`` template.
    493 
    494 We can use a similar idiom to alter the response returned by the generic view.
    495 If we wanted to provide a downloadable plain-text version of the list of
    496 authors, we could use a view like this::
    497 
    498     def author_list_plaintext(request):
    499         response = list_detail.object_list(
    500             request,
    501             queryset = Author.objects.all(),
    502             mimetype = "text/plain",
    503             template_name = "books/author_list.txt"
    504         )
    505         response["Content-Disposition"] = "attachment; filename=authors.txt"
    506         return response
    507 
    508 This works because the generic views return simple ``HttpResponse`` objects
    509 that can be treated like dictionaries to set HTTP headers. This
    510 ``Content-Disposition`` business, by the way, instructs the browser to
    511 download and save the page instead of displaying it in the browser.
  • docs/topics/http/generic-views.txt

    diff --git a/docs/topics/http/generic-views.txt b/docs/topics/http/generic-views.txt
    index 15f895e..fdaa27d 100644
    a b  
    22Generic views
    33=============
    44
    5 See :doc:`/ref/generic-views`.
     5See :doc:`/ref/class-based-views`.
  • docs/topics/http/urls.txt

    diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
    index 73c06ca..ee1d4e8 100644
    a b Old::  
    416416    from django.conf.urls import patterns, url, include
    417417
    418418    urlpatterns = patterns('',
    419         (r'^$', 'django.views.generic.date_based.archive_index'),
    420         (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'django.views.generic.date_based.archive_month'),
     419        (r'^$', 'myapp.views.app_index'),
     420        (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
    421421        (r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
    422422    )
    423423
    New::  
    425425
    426426    from django.conf.urls import patterns, url, include
    427427
    428     urlpatterns = patterns('django.views.generic.date_based',
    429         (r'^$', 'archive_index'),
    430         (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','archive_month'),
     428    urlpatterns = patterns('myapp.views',
     429        (r'^$', 'app_index'),
     430        (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
    431431    )
    432432
    433433    urlpatterns += patterns('weblog.views',
    In this example, for a request to ``/blog/2005/``, Django will call the  
    579579
    580580    year='2005', foo='bar'
    581581
    582 This technique is used in :doc:`generic views </ref/generic-views>` and in the
     582This technique is used in the
    583583:doc:`syndication framework </ref/contrib/syndication>` to pass metadata and
    584584options to views.
    585585
  • docs/topics/index.txt

    diff --git a/docs/topics/index.txt b/docs/topics/index.txt
    index 4b33099..95ca76e 100644
    a b Introductions to all the key parts of Django you'll need to know:  
    1212   forms/index
    1313   templates
    1414   class-based-views
    15    generic-views-migration
    1615   files
    1716   testing
    1817   auth
    Introductions to all the key parts of Django you'll need to know:  
    2726   serialization
    2827   settings
    2928   signals
    30 
    31 Deprecated features
    32 -------------------
    33 
    34 .. toctree::
    35    :maxdepth: 1
    36 
    37    generic-views
  • tests/regressiontests/special_headers/tests.py

    diff --git a/tests/regressiontests/special_headers/tests.py b/tests/regressiontests/special_headers/tests.py
    index 4de518c..087bfe9 100644
    a b  
    1 import warnings
    2 
    31from django.contrib.auth.models import User
    42from django.test import TestCase
    53
    class SpecialHeadersTest(TestCase):  
    86    fixtures = ['data.xml']
    97    urls = 'regressiontests.special_headers.urls'
    108
    11     def setUp(self):
    12         self.save_warnings_state()
    13         warnings.filterwarnings('ignore', category=DeprecationWarning,
    14                                 module='django.views.generic.list_detail')
    15 
    16     def tearDown(self):
    17         self.restore_warnings_state()
    18 
    199    def test_xheaders(self):
    2010        user = User.objects.get(username='super')
    2111        response = self.client.get('/special_headers/article/1/')
  • tests/regressiontests/special_headers/urls.py

    diff --git a/tests/regressiontests/special_headers/urls.py b/tests/regressiontests/special_headers/urls.py
    index 2e6a305..f7ba141 100644
    a b  
    22from __future__ import absolute_import
    33
    44from django.conf.urls import patterns
    5 from django.views.generic.list_detail import object_detail
    65
    76from . import views
    87from .models import Article
    98
    109urlpatterns = patterns('',
    11     (r'^special_headers/article/(?P<object_id>\d+)/$', object_detail, {'queryset': Article.objects.all()}),
     10    (r'^special_headers/article/(?P<object_id>\d+)/$', views.xview_xheaders),
    1211    (r'^special_headers/xview/func/$', views.xview_dec(views.xview)),
    1312    (r'^special_headers/xview/class/$', views.xview_dec(views.XViewClass.as_view())),
    1413)
  • tests/regressiontests/special_headers/views.py

    diff --git a/tests/regressiontests/special_headers/views.py b/tests/regressiontests/special_headers/views.py
    index ce94036..a8bbd65 100644
    a b  
    1 # -*- coding:utf-8 -*-
     1from django.core.xheaders import populate_xheaders
    22from django.http import HttpResponse
    33from django.utils.decorators import decorator_from_middleware
    44from django.views.generic import View
    55from django.middleware.doc import XViewMiddleware
    66
     7from .models import Article
     8
    79xview_dec = decorator_from_middleware(XViewMiddleware)
    810
    911def xview(request):
    1012    return HttpResponse()
    1113
     14def xview_xheaders(request, object_id):
     15    response = HttpResponse()
     16    populate_xheaders(request, response, Article, 1)
     17    return response
     18
    1219class XViewClass(View):
    1320    def get(self, request):
    1421        return HttpResponse()
  • tests/regressiontests/views/generic_urls.py

    diff --git a/tests/regressiontests/views/generic_urls.py b/tests/regressiontests/views/generic_urls.py
    index 40a6c01..5bf929e 100644
    a b  
    22from __future__ import absolute_import
    33
    44from django.conf.urls import patterns, url
     5from django.views.generic import RedirectView
    56
    67from . import views
    78from .models import Article, DateArticle, UrlArticle
    urlpatterns = patterns('',  
    3536    url(u'^中文/target/$', 'regressiontests.views.views.index_page'),
    3637)
    3738
    38 # Date-based generic views.
    39 urlpatterns += patterns('django.views.generic.date_based',
    40     (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/$',
    41         'object_detail',
    42         dict(slug_field='slug', **date_based_info_dict)),
    43     (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/allow_future/$',
    44         'object_detail',
    45         dict(allow_future=True, slug_field='slug', **date_based_info_dict)),
    46     (r'^date_based/archive_day/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$',
    47         'archive_day',
    48         numeric_days_info_dict),
    49     (r'^date_based/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$',
    50         'archive_month',
    51         date_based_info_dict),
    52     (r'^date_based/datefield/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$',
    53         'archive_month',
    54         date_based_datefield_info_dict),
    55 )
    56 
    57 # crud generic views.
    58 urlpatterns += patterns('django.views.generic.create_update',
    59     (r'^create_update/member/create/article/$', 'create_object',
    60         dict(login_required=True, model=Article)),
    61     (r'^create_update/create/article/$', 'create_object',
    62         dict(post_save_redirect='/create_update/view/article/%(slug)s/',
    63              model=Article)),
    64     (r'^create_update/update/article/(?P<slug>[-\w]+)/$', 'update_object',
    65         dict(post_save_redirect='/create_update/view/article/%(slug)s/',
    66              slug_field='slug', model=Article)),
    67     (r'^create_update/create_custom/article/$', views.custom_create),
    68     (r'^create_update/delete/article/(?P<slug>[-\w]+)/$', 'delete_object',
    69         dict(post_delete_redirect='/create_update/', slug_field='slug',
    70              model=Article)),
    71 
    72     # No post_save_redirect and no get_absolute_url on model.
    73     (r'^create_update/no_redirect/create/article/$', 'create_object',
    74         dict(model=Article)),
    75     (r'^create_update/no_redirect/update/article/(?P<slug>[-\w]+)/$',
    76         'update_object', dict(slug_field='slug', model=Article)),
    77 
    78     # get_absolute_url on model, but no passed post_save_redirect.
    79     (r'^create_update/no_url/create/article/$', 'create_object',
    80         dict(model=UrlArticle)),
    81     (r'^create_update/no_url/update/article/(?P<slug>[-\w]+)/$',
    82         'update_object', dict(slug_field='slug', model=UrlArticle)),
    83 )
    84 
    85 urlpatterns += patterns('django.views.generic.list_detail',
    86     (r'^object_list/page(?P<page>[\w]*)/$', 'object_list', object_list_dict),
    87     (r'^object_list_no_paginate_by/page(?P<page>[0-9]+)/$', 'object_list',
    88      object_list_no_paginate_by),
    89 )
    90 
    9139# rediriects, both temporary and permanent, with non-ASCII targets
    92 urlpatterns += patterns('django.views.generic.simple',
    93     ('^nonascii_redirect/$', 'redirect_to',
    94         {'url': u'/中文/target/', 'permanent': False}),
    95     ('^permanent_nonascii_redirect/$', 'redirect_to',
    96         {'url': u'/中文/target/', 'permanent': True}),
     40urlpatterns += patterns('',
     41    ('^nonascii_redirect/$', RedirectView.as_view(
     42        url=u'/中文/target/', permanent=False)),
     43    ('^permanent_nonascii_redirect/$', RedirectView.as_view(
     44        url=u'/中文/target/', permanent=True)),
    9745)
    9846
    9947urlpatterns += patterns('regressiontests.views.views',
    urlpatterns += patterns('regressiontests.views.views',  
    10755    (r'^shortcuts/render/current_app/$', 'render_view_with_current_app'),
    10856    (r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
    10957)
    110 
    111 # simple generic views.
    112 urlpatterns += patterns('django.views.generic.simple',
    113     (r'^simple/redirect_to/$', 'redirect_to', dict(url='/simple/target/')),
    114     (r'^simple/redirect_to_temp/$', 'redirect_to', dict(url='/simple/target/', permanent=False)),
    115     (r'^simple/redirect_to_none/$', 'redirect_to', dict(url=None)),
    116     (r'^simple/redirect_to_arg/(?P<id>\d+)/$', 'redirect_to', dict(url='/simple/target_arg/%(id)s/')),
    117     (r'^simple/redirect_to_query/$', 'redirect_to', dict(url='/simple/target/', query_string=True)),
    118     (r'^simple/redirect_to_arg_and_query/(?P<id>\d+)/$', 'redirect_to', dict(url='/simple/target_arg/%(id)s/', query_string=True)),
    119 )
  • tests/regressiontests/views/tests/__init__.py

    diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py
    index 04537a2..63db5da 100644
    a b from .debug import (DebugViewTests, ExceptionReporterTests,  
    44    ExceptionReporterTests, PlainTextReportTests, ExceptionReporterFilterTests,
    55    AjaxResponseExceptionReporterFilter)
    66from .defaults import DefaultsTests
    7 from .generic.create_update import (UpdateDeleteObjectTest, CreateObjectTest,
    8     PostSaveRedirectTests, NoPostSaveNoAbsoluteUrl, AbsoluteUrlNoPostSave)
    9 from .generic.date_based import MonthArchiveTest, ObjectDetailTest, DayArchiveTests
    10 from .generic.object_list import ObjectListTest
    11 from .generic.simple import RedirectToTest
    127from .i18n import JsI18NTests, I18NTests, JsI18NTestsMultiPackage
    138from .shortcuts import ShortcutTests
    149from .specials import URLHandling
  • deleted file tests/regressiontests/views/tests/generic/create_update.py

    diff --git a/tests/regressiontests/views/tests/generic/__init__.py b/tests/regressiontests/views/tests/generic/__init__.py
    deleted file mode 100644
    index e69de29..0000000
    diff --git a/tests/regressiontests/views/tests/generic/create_update.py b/tests/regressiontests/views/tests/generic/create_update.py
    deleted file mode 100644
    index 2abcfb4..0000000
    + -  
    1 import datetime
    2 import warnings
    3 
    4 from django.test import TestCase
    5 from django.core.exceptions import ImproperlyConfigured
    6 from regressiontests.views.models import Article, UrlArticle
    7 
    8 class CreateObjectTest(TestCase):
    9     fixtures = ['testdata.json']
    10     urls = 'regressiontests.views.generic_urls'
    11 
    12     def setUp(self):
    13         self.save_warnings_state()
    14         warnings.filterwarnings('ignore', category=DeprecationWarning,
    15                                 module='django.views.generic.create_update')
    16 
    17     def tearDown(self):
    18         self.restore_warnings_state()
    19 
    20     def test_login_required_view(self):
    21         """
    22         Verifies that an unauthenticated user attempting to access a
    23         login_required view gets redirected to the login page and that
    24         an authenticated user is let through.
    25         """
    26         view_url = '/create_update/member/create/article/'
    27         response = self.client.get(view_url)
    28         self.assertRedirects(response, '/accounts/login/?next=%s' % view_url)
    29         # Now login and try again.
    30         login = self.client.login(username='testclient', password='password')
    31         self.assertTrue(login, 'Could not log in')
    32         response = self.client.get(view_url)
    33         self.assertEqual(response.status_code, 200)
    34         self.assertTemplateUsed(response, 'views/article_form.html')
    35 
    36     def test_create_article_display_page(self):
    37         """
    38         Ensures the generic view returned the page and contains a form.
    39         """
    40         view_url = '/create_update/create/article/'
    41         response = self.client.get(view_url)
    42         self.assertEqual(response.status_code, 200)
    43         self.assertTemplateUsed(response, 'views/article_form.html')
    44         if not response.context.get('form'):
    45             self.fail('No form found in the response.')
    46 
    47     def test_create_article_with_errors(self):
    48         """
    49         POSTs a form that contains validation errors.
    50         """
    51         view_url = '/create_update/create/article/'
    52         num_articles = Article.objects.count()
    53         response = self.client.post(view_url, {
    54             'title': 'My First Article',
    55         })
    56         self.assertFormError(response, 'form', 'slug', [u'This field is required.'])
    57         self.assertTemplateUsed(response, 'views/article_form.html')
    58         self.assertEqual(num_articles, Article.objects.count(),
    59                          "Number of Articles should not have changed.")
    60 
    61     def test_create_custom_save_article(self):
    62         """
    63         Creates a new article using a custom form class with a save method
    64         that alters the slug entered.
    65         """
    66         view_url = '/create_update/create_custom/article/'
    67         response = self.client.post(view_url, {
    68             'title': 'Test Article',
    69             'slug': 'this-should-get-replaced',
    70             'author': 1,
    71             'date_created': datetime.datetime(2007, 6, 25),
    72         })
    73         self.assertRedirects(response,
    74             '/create_update/view/article/some-other-slug/',
    75             target_status_code=404)
    76 
    77 class UpdateDeleteObjectTest(TestCase):
    78     fixtures = ['testdata.json']
    79     urls = 'regressiontests.views.generic_urls'
    80 
    81     def setUp(self):
    82         self.save_warnings_state()
    83         warnings.filterwarnings('ignore', category=DeprecationWarning,
    84                                 module='django.views.generic.create_update')
    85 
    86     def tearDown(self):
    87         self.restore_warnings_state()
    88 
    89     def test_update_object_form_display(self):
    90         """
    91         Verifies that the form was created properly and with initial values.
    92         """
    93         response = self.client.get('/create_update/update/article/old_article/')
    94         self.assertTemplateUsed(response, 'views/article_form.html')
    95         self.assertHTMLEqual(unicode(response.context['form']['title']),
    96             u'<input id="id_title" type="text" name="title" value="Old Article" maxlength="100" />')
    97 
    98     def test_update_object(self):
    99         """
    100         Verifies the updating of an Article.
    101         """
    102         response = self.client.post('/create_update/update/article/old_article/', {
    103             'title': 'Another Article',
    104             'slug': 'another-article-slug',
    105             'author': 1,
    106             'date_created': datetime.datetime(2007, 6, 25),
    107         })
    108         article = Article.objects.get(pk=1)
    109         self.assertEqual(article.title, "Another Article")
    110 
    111     def test_delete_object_confirm(self):
    112         """
    113         Verifies the confirm deletion page is displayed using a GET.
    114         """
    115         response = self.client.get('/create_update/delete/article/old_article/')
    116         self.assertTemplateUsed(response, 'views/article_confirm_delete.html')
    117 
    118     def test_delete_object(self):
    119         """
    120         Verifies the object actually gets deleted on a POST.
    121         """
    122         view_url = '/create_update/delete/article/old_article/'
    123         response = self.client.post(view_url)
    124         try:
    125             Article.objects.get(slug='old_article')
    126         except Article.DoesNotExist:
    127             pass
    128         else:
    129             self.fail('Object was not deleted.')
    130 
    131 class PostSaveRedirectTests(TestCase):
    132     """
    133     Verifies that the views redirect to the correct locations depending on
    134     if a post_save_redirect was passed and a get_absolute_url method exists
    135     on the Model.
    136     """
    137 
    138     fixtures = ['testdata.json']
    139     article_model = Article
    140     urls = 'regressiontests.views.generic_urls'
    141 
    142     create_url = '/create_update/create/article/'
    143     update_url = '/create_update/update/article/old_article/'
    144     delete_url = '/create_update/delete/article/old_article/'
    145 
    146     create_redirect = '/create_update/view/article/my-first-article/'
    147     update_redirect = '/create_update/view/article/another-article-slug/'
    148     delete_redirect = '/create_update/'
    149 
    150     def setUp(self):
    151         self.save_warnings_state()
    152         warnings.filterwarnings('ignore', category=DeprecationWarning,
    153                                 module='django.views.generic.create_update')
    154 
    155     def tearDown(self):
    156         self.restore_warnings_state()
    157 
    158     def test_create_article(self):
    159         num_articles = self.article_model.objects.count()
    160         response = self.client.post(self.create_url, {
    161             'title': 'My First Article',
    162             'slug': 'my-first-article',
    163             'author': '1',
    164             'date_created': datetime.datetime(2007, 6, 25),
    165         })
    166         self.assertRedirects(response, self.create_redirect,
    167                              target_status_code=404)
    168         self.assertEqual(num_articles + 1, self.article_model.objects.count(),
    169                          "A new Article should have been created.")
    170 
    171     def test_update_article(self):
    172         num_articles = self.article_model.objects.count()
    173         response = self.client.post(self.update_url, {
    174             'title': 'Another Article',
    175             'slug': 'another-article-slug',
    176             'author': 1,
    177             'date_created': datetime.datetime(2007, 6, 25),
    178         })
    179         self.assertRedirects(response, self.update_redirect,
    180                              target_status_code=404)
    181         self.assertEqual(num_articles, self.article_model.objects.count(),
    182                          "A new Article should not have been created.")
    183 
    184     def test_delete_article(self):
    185         num_articles = self.article_model.objects.count()
    186         response = self.client.post(self.delete_url)
    187         self.assertRedirects(response, self.delete_redirect,
    188                              target_status_code=404)
    189         self.assertEqual(num_articles - 1, self.article_model.objects.count(),
    190                          "An Article should have been deleted.")
    191 
    192 class NoPostSaveNoAbsoluteUrl(PostSaveRedirectTests):
    193     """
    194     Tests that when no post_save_redirect is passed and no get_absolute_url
    195     method exists on the Model that the view raises an ImproperlyConfigured
    196     error.
    197     """
    198     urls = 'regressiontests.views.generic_urls'
    199 
    200     create_url = '/create_update/no_redirect/create/article/'
    201     update_url = '/create_update/no_redirect/update/article/old_article/'
    202 
    203     def setUp(self):
    204         self.save_warnings_state()
    205         warnings.filterwarnings('ignore', category=DeprecationWarning,
    206                                 module='django.views.generic.create_update')
    207 
    208     def tearDown(self):
    209         self.restore_warnings_state()
    210 
    211     def test_create_article(self):
    212         self.assertRaises(ImproperlyConfigured,
    213             super(NoPostSaveNoAbsoluteUrl, self).test_create_article)
    214 
    215     def test_update_article(self):
    216         self.assertRaises(ImproperlyConfigured,
    217             super(NoPostSaveNoAbsoluteUrl, self).test_update_article)
    218 
    219     def test_delete_article(self):
    220         """
    221         The delete_object view requires a post_delete_redirect, so skip testing
    222         here.
    223         """
    224         pass
    225 
    226 class AbsoluteUrlNoPostSave(PostSaveRedirectTests):
    227     """
    228     Tests that the views redirect to the Model's get_absolute_url when no
    229     post_save_redirect is passed.
    230     """
    231     urls = 'regressiontests.views.generic_urls'
    232 
    233     # Article model with get_absolute_url method.
    234     article_model = UrlArticle
    235 
    236     create_url = '/create_update/no_url/create/article/'
    237     update_url = '/create_update/no_url/update/article/old_article/'
    238 
    239     create_redirect = '/urlarticles/my-first-article/'
    240     update_redirect = '/urlarticles/another-article-slug/'
    241 
    242     def setUp(self):
    243         self.save_warnings_state()
    244         warnings.filterwarnings('ignore', category=DeprecationWarning,
    245                                 module='django.views.generic.create_update')
    246 
    247     def tearDown(self):
    248         self.restore_warnings_state()
    249 
    250     def test_delete_article(self):
    251         """
    252         The delete_object view requires a post_delete_redirect, so skip testing
    253         here.
    254         """
    255         pass
  • deleted file tests/regressiontests/views/tests/generic/date_based.py

    diff --git a/tests/regressiontests/views/tests/generic/date_based.py b/tests/regressiontests/views/tests/generic/date_based.py
    deleted file mode 100644
    index 96555e5..0000000
    + -  
    1 # coding: utf-8
    2 import warnings
    3 
    4 from django.test import TestCase
    5 from datetime import datetime, date
    6 from datetime import timedelta
    7 from regressiontests.views.models import Article, Author, DateArticle
    8 
    9 class ObjectDetailTest(TestCase):
    10     fixtures = ['testdata.json']
    11     urls = 'regressiontests.views.generic_urls'
    12 
    13     def setUp(self):
    14         self.save_warnings_state()
    15         warnings.filterwarnings('ignore', category=DeprecationWarning,
    16                                 module='django.views.generic.date_based')
    17         # Correct the date for the current article
    18         current_article = Article.objects.get(title="Current Article")
    19         current_article.date_created = datetime.now()
    20         current_article.save()
    21 
    22     def tearDown(self):
    23         self.restore_warnings_state()
    24 
    25     def test_finds_past(self):
    26         "date_based.object_detail can view a page in the past"
    27         response = self.client.get('/date_based/object_detail/2001/01/01/old_article/')
    28         self.assertEqual(response.status_code, 200)
    29         self.assertEqual(response.context['object'].title, "Old Article")
    30 
    31     def test_object_detail_finds_today(self):
    32         "date_based.object_detail can view a page from today"
    33         today_url = datetime.now().strftime('%Y/%m/%d')
    34         response = self.client.get('/date_based/object_detail/%s/current_article/' % today_url)
    35         self.assertEqual(response.status_code, 200)
    36         self.assertEqual(response.context['object'].title, "Current Article")
    37 
    38     def test_object_detail_ignores_future(self):
    39         "date_based.object_detail can view a page from the future, but only if allowed."
    40         response = self.client.get('/date_based/object_detail/3000/01/01/future_article/')
    41         self.assertEqual(response.status_code, 404)
    42 
    43     def test_object_detail_allowed_future_if_enabled(self):
    44         "date_based.object_detail can view a page from the future if explicitly allowed."
    45         response = self.client.get('/date_based/object_detail/3000/01/01/future_article/allow_future/')
    46         self.assertEqual(response.status_code, 200)
    47         self.assertEqual(response.context['object'].title, "Future Article")
    48 
    49 class MonthArchiveTest(TestCase):
    50     urls = 'regressiontests.views.generic_urls'
    51 
    52     def setUp(self):
    53         self.save_warnings_state()
    54         warnings.filterwarnings('ignore', category=DeprecationWarning,
    55                                 module='django.views.generic.date_based')
    56 
    57     def tearDown(self):
    58         self.restore_warnings_state()
    59 
    60     def test_archive_month_includes_only_month(self):
    61         "Regression for #3031: Archives around Feburary include only one month"
    62         author = Author(name="John Smith")
    63         author.save()
    64 
    65         # 2004 was a leap year, so it should be weird enough to not cheat
    66         first_second_of_feb = datetime(2004, 2, 1, 0, 0, 1)
    67         first_second_of_mar = datetime(2004, 3, 1, 0, 0, 1)
    68         two_seconds = timedelta(0, 2, 0)
    69         article = Article(title="example", author=author)
    70 
    71         article.date_created = first_second_of_feb
    72         article.save()
    73         response = self.client.get('/date_based/archive_month/2004/02/')
    74         self.assertEqual(response.status_code, 200)
    75         self.assertEqual(response.context['next_month'], date(2004, 3, 1))
    76         self.assertEqual(response.context['previous_month'], date(2004, 1, 1))
    77 
    78         article.date_created = first_second_of_feb-two_seconds
    79         article.save()
    80         response = self.client.get('/date_based/archive_month/2004/02/')
    81         self.assertEqual(response.status_code, 404)
    82 
    83         article.date_created = first_second_of_mar-two_seconds
    84         article.save()
    85         response = self.client.get('/date_based/archive_month/2004/02/')
    86         self.assertEqual(response.status_code, 200)
    87         self.assertEqual(response.context['next_month'], date(2004, 3, 1))
    88         self.assertEqual(response.context['previous_month'], date(2004, 1, 1))
    89 
    90         article.date_created = first_second_of_mar
    91         article.save()
    92         response = self.client.get('/date_based/archive_month/2004/02/')
    93         self.assertEqual(response.status_code, 404)
    94 
    95         article2 = DateArticle(title="example", author=author)
    96 
    97         article2.date_created = first_second_of_feb.date()
    98         article2.save()
    99         response = self.client.get('/date_based/datefield/archive_month/2004/02/')
    100         self.assertEqual(response.status_code, 200)
    101         self.assertEqual(response.context['next_month'], date(2004, 3, 1))
    102         self.assertEqual(response.context['previous_month'], date(2004, 1, 1))
    103 
    104         article2.date_created = (first_second_of_feb-two_seconds).date()
    105         article2.save()
    106         response = self.client.get('/date_based/datefield/archive_month/2004/02/')
    107         self.assertEqual(response.status_code, 404)
    108 
    109         article2.date_created = (first_second_of_mar-two_seconds).date()
    110         article2.save()
    111         response = self.client.get('/date_based/datefield/archive_month/2004/02/')
    112         self.assertEqual(response.status_code, 200)
    113         self.assertEqual(response.context['next_month'], date(2004, 3, 1))
    114         self.assertEqual(response.context['previous_month'], date(2004, 1, 1))
    115 
    116         article2.date_created = first_second_of_mar.date()
    117         article2.save()
    118         response = self.client.get('/date_based/datefield/archive_month/2004/02/')
    119         self.assertEqual(response.status_code, 404)
    120 
    121         now = datetime.now()
    122         prev_month = now.date().replace(day=1)
    123         if prev_month.month == 1:
    124             prev_month = prev_month.replace(year=prev_month.year-1, month=12)
    125         else:
    126             prev_month = prev_month.replace(month=prev_month.month-1)
    127         article2.date_created = now
    128         article2.save()
    129         response = self.client.get('/date_based/datefield/archive_month/%s/' % now.strftime('%Y/%m'))
    130         self.assertEqual(response.status_code, 200)
    131         self.assertEqual(response.context['next_month'], None)
    132         self.assertEqual(response.context['previous_month'], prev_month)
    133        
    134     def test_archive_month_date_list(self):
    135         author = Author(name="John Smith")
    136         author.save()
    137         date1 = datetime(2010, 1, 1, 0, 0, 0)
    138         date2 = datetime(2010, 1, 2, 0, 0, 0)
    139         Article.objects.create(title='example1', author=author, date_created=date1)
    140         Article.objects.create(title='example2', author=author, date_created=date2)
    141         response = self.client.get('/date_based/archive_month/2010/1/')
    142         self.assertEqual(response.status_code, 200)
    143         self.assertEqual(len(response.context['date_list']), 2)
    144         self.assertEqual(response.context['date_list'][0], date1)
    145         # Checks that the same date is not included more than once in the list
    146         Article.objects.create(title='example2', author=author, date_created=date2)
    147         response = self.client.get('/date_based/archive_month/2010/1/')
    148         self.assertEqual(len(response.context['date_list']), 2)
    149 
    150 class DayArchiveTests(TestCase):
    151     urls = 'regressiontests.views.generic_urls'
    152 
    153     def setUp(self):
    154         self.save_warnings_state()
    155         warnings.filterwarnings('ignore', category=DeprecationWarning,
    156                                 module='django.views.generic.date_based')
    157         warnings.filterwarnings('ignore', category=DeprecationWarning,
    158                                 module='django.views.generic.create_update')
    159 
    160     def tearDown(self):
    161         self.restore_warnings_state()
    162 
    163     def test_year_month_day_format(self):
    164         """
    165         Make sure day views don't get confused with numeric month formats (#7944)
    166         """
    167         author = Author.objects.create(name="John Smith")
    168         article = Article.objects.create(title="example", author=author, date_created=datetime(2004, 1, 21, 0, 0, 1))
    169         response = self.client.get('/date_based/archive_day/2004/1/21/')
    170         self.assertEqual(response.status_code, 200)
    171         self.assertEqual(response.context['object_list'][0], article)
  • deleted file tests/regressiontests/views/tests/generic/object_list.py

    diff --git a/tests/regressiontests/views/tests/generic/object_list.py b/tests/regressiontests/views/tests/generic/object_list.py
    deleted file mode 100644
    index 3fa871a..0000000
    + -  
    1 import warnings
    2 
    3 from django.test import TestCase
    4 
    5 
    6 class ObjectListTest(TestCase):
    7     fixtures = ['testdata.json']
    8     urls = 'regressiontests.views.generic_urls'
    9 
    10     def setUp(self):
    11         self.save_warnings_state()
    12         warnings.filterwarnings('ignore', category=DeprecationWarning,
    13                                 module='django.views.generic.list_detail')
    14 
    15     def tearDown(self):
    16         self.restore_warnings_state()
    17 
    18     def check_pagination(self, url, expected_status_code, object_count=None):
    19         response = self.client.get(url)
    20         self.assertEqual(response.status_code, expected_status_code)
    21 
    22         if object_count:
    23             self.assertEqual(response.context['is_paginated'], True)
    24             self.assertEqual(len(response.context['page_obj'].object_list),
    25                              object_count)
    26 
    27         return response
    28 
    29     def test_finds_pages(self):
    30         # Check page count doesn't start at 0.
    31         self.check_pagination('/object_list/page0/', 404)
    32 
    33         # Check basic pages.
    34         self.check_pagination('/object_list/page/', 200, 2)
    35         self.check_pagination('/object_list/page1/', 200, 2)
    36         self.check_pagination('/object_list/page2/', 200, 1)
    37         self.check_pagination('/object_list/page3/', 404)
    38 
    39         # Check the special "last" page.
    40         self.check_pagination('/object_list/pagelast/', 200, 1)
    41         self.check_pagination('/object_list/pagenotlast/', 404)
    42 
    43     def test_no_paginate_by(self):
    44         # Ensure that the view isn't paginated by default.
    45         url = '/object_list_no_paginate_by/page1/'
    46         response = self.check_pagination(url, 200)
    47         self.assertEqual(response.context['is_paginated'], False)
  • deleted file tests/regressiontests/views/tests/generic/simple.py

    diff --git a/tests/regressiontests/views/tests/generic/simple.py b/tests/regressiontests/views/tests/generic/simple.py
    deleted file mode 100644
    index 7dcf08a..0000000
    + -  
    1 # coding: utf-8
    2 import warnings
    3 
    4 from django.test import TestCase
    5 
    6 class RedirectToTest(TestCase):
    7     urls = 'regressiontests.views.generic_urls'
    8 
    9     def setUp(self):
    10         self.save_warnings_state()
    11         warnings.filterwarnings('ignore', category=DeprecationWarning,
    12                                 module='django.views.generic.simple')
    13 
    14     def tearDown(self):
    15         self.restore_warnings_state()
    16 
    17     def test_redirect_to_returns_permanent_redirect(self):
    18         "simple.redirect_to returns a permanent redirect (301) by default"
    19         response = self.client.get('/simple/redirect_to/')
    20         self.assertEqual(response.status_code, 301)
    21         self.assertEqual('http://testserver/simple/target/', response['Location'])
    22 
    23     def test_redirect_to_can_return_a_temporary_redirect(self):
    24         "simple.redirect_to returns a temporary redirect (302) when explicitely asked to"
    25         response = self.client.get('/simple/redirect_to_temp/')
    26         self.assertEqual(response.status_code, 302)
    27         self.assertEqual('http://testserver/simple/target/', response['Location'])
    28 
    29     def test_redirect_to_on_empty_url_returns_gone(self):
    30         "simple.redirect_to returns resource gone (410) when given a None url"
    31         response = self.client.get('/simple/redirect_to_none/')
    32         self.assertEqual(response.status_code, 410)
    33 
    34     def test_redirect_to_allows_formatted_url_string(self):
    35         "simple.redirect_to uses string interpolation on target url for keyword args"
    36         response = self.client.get('/simple/redirect_to_arg/42/')
    37         self.assertEqual(response.status_code, 301)
    38         self.assertEqual('http://testserver/simple/target_arg/42/', response['Location'])
    39 
    40     def test_redirect_to_allows_query_string_to_be_passed(self):
    41         "simple.redirect_to configured with query_string=True passes on any query string"
    42         # the default is to not forward the query string
    43         response = self.client.get('/simple/redirect_to/?param1=foo&param2=bar')
    44         self.assertEqual(response.status_code, 301)
    45         self.assertEqual('http://testserver/simple/target/', response['Location'])
    46         # views configured with query_string=True however passes the query string along
    47         response = self.client.get('/simple/redirect_to_query/?param1=foo&param2=bar')
    48         self.assertEqual(response.status_code, 301)
    49         self.assertEqual('http://testserver/simple/target/?param1=foo&param2=bar', response['Location'])
    50 
    51         # Confirm that the contents of the query string are not subject to
    52         # string interpolation (Refs #17111):
    53         response = self.client.get('/simple/redirect_to_query/?param1=foo&param2=hist%C3%B3ria')
    54         self.assertEqual(response.status_code, 301)
    55         self.assertEqual('http://testserver/simple/target/?param1=foo&param2=hist%C3%B3ria', response['Location'])
    56         response = self.client.get('/simple/redirect_to_arg_and_query/99/?param1=foo&param2=hist%C3%B3ria')
    57         self.assertEqual(response.status_code, 301)
    58         self.assertEqual('http://testserver/simple/target_arg/99/?param1=foo&param2=hist%C3%B3ria', response['Location'])
    59 
    60     def test_redirect_to_when_meta_contains_no_query_string(self):
    61         "regression for #16705"
    62         # we can't use self.client.get because it always sets QUERY_STRING
    63         response = self.client.request(PATH_INFO='/simple/redirect_to/')
    64         self.assertEqual(response.status_code, 301)
  • tests/regressiontests/views/tests/shortcuts.py

    diff --git a/tests/regressiontests/views/tests/shortcuts.py b/tests/regressiontests/views/tests/shortcuts.py
    index d20b6d1..a48bb25 100644
    a b  
    1 import warnings
    2 
    31from django.conf import settings
    42from django.test import TestCase
    53from django.test.utils import override_settings
    from django.test.utils import override_settings  
    119class ShortcutTests(TestCase):
    1210    urls = 'regressiontests.views.generic_urls'
    1311
    14     def setUp(self):
    15         self.save_warnings_state()
    16         warnings.filterwarnings('ignore', category=DeprecationWarning,
    17                                 module='django.views.generic.simple')
    18 
    19     def tearDown(self):
    20         self.restore_warnings_state()
    21 
    2212    def test_render_to_response(self):
    2313        response = self.client.get('/shortcuts/render_to_response/')
    2414        self.assertEqual(response.status_code, 200)
  • tests/regressiontests/views/tests/specials.py

    diff --git a/tests/regressiontests/views/tests/specials.py b/tests/regressiontests/views/tests/specials.py
    index 7855704..cd8ac5b 100644
    a b  
    11# coding: utf-8
    2 import warnings
    3 
    42from django.test import TestCase
    53
    64
    class URLHandling(TestCase):  
    119    urls = 'regressiontests.views.generic_urls'
    1210    redirect_target = "/%E4%B8%AD%E6%96%87/target/"
    1311
    14     def setUp(self):
    15         self.save_warnings_state()
    16         warnings.filterwarnings('ignore', category=DeprecationWarning,
    17                                 module='django.views.generic.simple')
    18 
    19     def tearDown(self):
    20         self.restore_warnings_state()
    21 
    2212    def test_combining_redirect(self):
    2313        """
    2414        Tests that redirecting to an IRI, requiring encoding before we use it
  • tests/regressiontests/views/views.py

    diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
    index 1d17e6e..8e530cd 100644
    a b def index_page(request):  
    2121    """Dummy index page"""
    2222    return HttpResponse('<html><body>Dummy page</body></html>')
    2323
    24 def custom_create(request):
    25     """
    26     Calls create_object generic view with a custom form class.
    27     """
    28     class SlugChangingArticleForm(forms.ModelForm):
    29         """Custom form class to overwrite the slug."""
    30 
    31         class Meta:
    32             model = Article
    33 
    34         def save(self, *args, **kwargs):
    35             self.instance.slug = 'some-other-slug'
    36             return super(SlugChangingArticleForm, self).save(*args, **kwargs)
    37 
    38     from django.views.generic.create_update import create_object
    39     return create_object(request,
    40         post_save_redirect='/create_update/view/article/%(slug)s/',
    41         form_class=SlugChangingArticleForm)
    42 
    4324def raises(request):
    4425    # Make sure that a callable that raises an exception in the stack frame's
    4526    # local vars won't hijack the technical 500 response. See:
Back to Top