Django

Code

Changeset 7952

Show
Ignore:
Timestamp:
07/18/08 14:45:00 (3 months ago)
Author:
jacob
Message:

Fixed #3639: updated generic create_update views to use newforms. This is a backwards-incompatible change.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/views/generic/create_update.py

    r5877 r7952  
     1from django.newforms.models import ModelFormMetaclass, ModelForm 
     2from django.template import RequestContext, loader 
     3from django.http import Http404, HttpResponse, HttpResponseRedirect 
    14from django.core.xheaders import populate_xheaders 
    2 from django.template import loader 
    3 from django import oldforms 
    4 from django.db.models import FileField 
    5 from django.contrib.auth.views import redirect_to_login 
    6 from django.template import RequestContext 
    7 from django.http import Http404, HttpResponse, HttpResponseRedirect 
    85from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured 
    96from django.utils.translation import ugettext 
    10  
    11 def create_object(request, model, template_name=None, 
     7from django.contrib.auth.views import redirect_to_login 
     8from django.views.generic import GenericViewError 
     9 
     10def deprecate_follow(follow): 
     11    """ 
     12    Issues a DeprecationWarning if follow is anything but None. 
     13 
     14    The old Manipulator-based forms used a follow argument that is no longer 
     15    needed for newforms-based forms. 
     16    """ 
     17    if follow is not None: 
     18        import warning 
     19        msg = ("Generic views have been changed to use newforms, and the" 
     20               "'follow' argument is no longer used.  Please update your code" 
     21               "to not use the 'follow' argument.") 
     22        warning.warn(msg, DeprecationWarning, stacklevel=3) 
     23 
     24def apply_extra_context(extra_context, context): 
     25    """ 
     26    Adds items from extra_context dict to context.  If a value in extra_context 
     27    is callable, then it is called and the result is added to context. 
     28    """ 
     29    for key, value in extra_context.iteritems(): 
     30        if callable(value): 
     31            context[key] = value() 
     32        else: 
     33            context[key] = value 
     34 
     35def get_model_and_form_class(model, form_class): 
     36    """ 
     37    Returns a model and form class based on the model and form_class 
     38    parameters that were passed to the generic view. 
     39 
     40    If ``form_class`` is given then its associated model will be returned along 
     41    with ``form_class`` itself.  Otherwise, if ``model`` is given, ``model`` 
     42    itself will be returned along with a ``ModelForm`` class created from 
     43    ``model``. 
     44    """ 
     45    if form_class: 
     46        return form_class._meta.model, form_class 
     47    if model: 
     48        # The inner Meta class fails if model = model is used for some reason. 
     49        tmp_model = model 
     50        # TODO: we should be able to construct a ModelForm without creating 
     51        # and passing in a temporary inner class. 
     52        class Meta: 
     53            model = tmp_model 
     54        class_name = model.__name__ + 'Form' 
     55        form_class = ModelFormMetaclass(class_name, (ModelForm,), {'Meta': Meta}) 
     56        return model, form_class 
     57    raise GenericViewError("Generic view must be called with either a model or" 
     58                           " form_class argument.") 
     59 
     60def redirect(post_save_redirect, obj): 
     61    """ 
     62    Returns a HttpResponseRedirect to ``post_save_redirect``. 
     63 
     64    ``post_save_redirect`` should be a string, and can contain named string- 
     65    substitution place holders of ``obj`` field names. 
     66 
     67    If ``post_save_redirect`` is None, then redirect to ``obj``'s URL returned 
     68    by ``get_absolute_url()``.  If ``obj`` has no ``get_absolute_url`` method, 
     69    then raise ImproperlyConfigured. 
     70 
     71    This method is meant to handle the post_save_redirect parameter to the 
     72    ``create_object`` and ``update_object`` views. 
     73    """ 
     74    if post_save_redirect: 
     75        return HttpResponseRedirect(post_save_redirect % obj.__dict__) 
     76    elif hasattr(obj, 'get_absolute_url'): 
     77        return HttpResponseRedirect(obj.get_absolute_url()) 
     78    else: 
     79        raise ImproperlyConfigured( 
     80            "No URL to redirect to.  Either pass a post_save_redirect" 
     81            " parameter to the generic view or define a get_absolute_url" 
     82            " method on the Model.") 
     83 
     84def lookup_object(model, object_id, slug, slug_field): 
     85    """ 
     86    Return the ``model`` object with the passed ``object_id``.  If 
     87    ``object_id`` is None, then return the the object whose ``slug_field`` 
     88    equals the passed ``slug``.  If ``slug`` and ``slug_field`` are not passed, 
     89    then raise Http404 exception. 
     90    """ 
     91    lookup_kwargs = {} 
     92    if object_id: 
     93        lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id 
     94    elif slug and slug_field: 
     95        lookup_kwargs['%s__exact' % slug_field] = slug 
     96    else: 
     97        raise GenericViewError( 
     98            "Generic view must be called with either an object_id or a" 
     99            " slug/slug_field.") 
     100    try: 
     101        return model.objects.get(**lookup_kwargs) 
     102    except ObjectDoesNotExist: 
     103        raise Http404("No %s found for %s" 
     104                      % (model._meta.verbose_name, lookup_kwargs)) 
     105 
     106def create_object(request, model=None, template_name=None, 
    12107        template_loader=loader, extra_context=None, post_save_redirect=None, 
    13         login_required=False, follow=None, context_processors=None): 
     108        login_required=False, follow=None, context_processors=None, 
     109        form_class=None): 
    14110    """ 
    15111    Generic object-creation function. 
     
    18114    Context: 
    19115        form 
    20             the form wrapper for the object 
    21     """ 
     116            the form for the object 
     117    """ 
     118    deprecate_follow(follow) 
    22119    if extra_context is None: extra_context = {} 
    23120    if login_required and not request.user.is_authenticated(): 
    24121        return redirect_to_login(request.path) 
    25122 
    26     manipulator = model.AddManipulator(follow=follow) 
    27     if request.POST: 
    28         # If data was POSTed, we're trying to create a new object 
    29         new_data = request.POST.copy() 
    30  
    31         if model._meta.has_field_type(FileField): 
    32             new_data.update(request.FILES) 
    33  
    34         # Check for errors 
    35         errors = manipulator.get_validation_errors(new_data) 
    36         manipulator.do_html2python(new_data) 
    37  
    38         if not errors: 
    39             # No errors -- this means we can save the data! 
    40             new_object = manipulator.save(new_data) 
    41  
     123    model, form_class = get_model_and_form_class(model, form_class) 
     124    if request.method == 'POST': 
     125        form = form_class(request.POST, request.FILES) 
     126        if form.is_valid(): 
     127            new_object = form.save() 
    42128            if request.user.is_authenticated(): 
    43129                request.user.message_set.create(message=ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 
    44  
    45             # Redirect to the new object: first by trying post_save_redirect, 
    46             # then by obj.get_absolute_url; fail if neither works. 
    47             if post_save_redirect: 
    48                 return HttpResponseRedirect(post_save_redirect % new_object.__dict__) 
    49             elif hasattr(new_object, 'get_absolute_url'): 
    50                 return HttpResponseRedirect(new_object.get_absolute_url()) 
    51             else: 
    52                 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 
    53     else: 
    54         # No POST, so we want a brand new form without any data or errors 
    55         errors = {} 
    56         new_data = manipulator.flatten_data() 
    57  
    58     # Create the FormWrapper, template, context, response 
    59     form = oldforms.FormWrapper(manipulator, new_data, errors) 
     130            return redirect(post_save_redirect, new_object) 
     131    else: 
     132        form = form_class() 
     133 
     134    # Create the template, context, response 
    60135    if not template_name: 
    61136        template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 
     
    64139        'form': form, 
    65140    }, context_processors) 
    66     for key, value in extra_context.items(): 
    67         if callable(value): 
    68             c[key] = value() 
    69         else: 
    70             c[key] = value 
     141    apply_extra_context(extra_context, c) 
    71142    return HttpResponse(t.render(c)) 
    72143 
    73 def update_object(request, model, object_id=None, slug=None, 
     144def update_object(request, model=None, object_id=None, slug=None, 
    74145        slug_field='slug', template_name=None, template_loader=loader, 
    75146        extra_context=None, post_save_redirect=None, 
    76147        login_required=False, follow=None, context_processors=None, 
    77         template_object_name='object'): 
     148        template_object_name='object', form_class=None): 
    78149    """ 
    79150    Generic object-update function. 
     
    82153    Context: 
    83154        form 
    84             the form wrapper for the object 
     155            the form for the object 
    85156        object 
    86157            the original object being edited 
    87158    """ 
     159    deprecate_follow(follow) 
    88160    if extra_context is None: extra_context = {} 
    89161    if login_required and not request.user.is_authenticated(): 
    90162        return redirect_to_login(request.path) 
    91163 
    92     # Look up the object to be edited 
    93     lookup_kwargs = {} 
    94     if object_id: 
    95         lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id 
    96     elif slug and slug_field: 
    97         lookup_kwargs['%s__exact' % slug_field] = slug 
    98     else: 
    99         raise AttributeError("Generic edit view must be called with either an object_id or a slug/slug_field") 
    100     try: 
    101         object = model.objects.get(**lookup_kwargs) 
    102     except ObjectDoesNotExist: 
    103         raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs) 
    104  
    105     manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow) 
    106  
    107     if request.POST: 
    108         new_data = request.POST.copy() 
    109         if model._meta.has_field_type(FileField): 
    110             new_data.update(request.FILES) 
    111         errors = manipulator.get_validation_errors(new_data) 
    112         manipulator.do_html2python(new_data) 
    113         if not errors: 
    114             object = manipulator.save(new_data) 
    115  
     164    model, form_class = get_model_and_form_class(model, form_class) 
     165    obj = lookup_object(model, object_id, slug, slug_field) 
     166 
     167    if request.method == 'POST': 
     168        form = form_class(request.POST, request.FILES, instance=obj) 
     169        if form.is_valid(): 
     170            obj = form.save() 
    116171            if request.user.is_authenticated(): 
    117172                request.user.message_set.create(message=ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) 
    118  
    119             # Do a post-after-redirect so that reload works, etc. 
    120             if post_save_redirect: 
    121                 return HttpResponseRedirect(post_save_redirect % object.__dict__) 
    122             elif hasattr(object, 'get_absolute_url'): 
    123                 return HttpResponseRedirect(object.get_absolute_url()) 
    124             else: 
    125                 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 
    126     else: 
    127         errors = {} 
    128         # This makes sure the form acurate represents the fields of the place. 
    129         new_data = manipulator.flatten_data() 
    130  
    131     form = oldforms.FormWrapper(manipulator, new_data, errors) 
     173            return redirect(post_save_redirect, obj) 
     174    else: 
     175        form = form_class(instance=obj) 
     176 
    132177    if not template_name: 
    133178        template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 
     
    135180    c = RequestContext(request, { 
    136181        'form': form, 
    137         template_object_name: object
     182        template_object_name: obj
    138183    }, context_processors) 
    139     for key, value in extra_context.items(): 
    140         if callable(value): 
    141             c[key] = value() 
    142         else: 
    143             c[key] = value 
     184    apply_extra_context(extra_context, c) 
    144185    response = HttpResponse(t.render(c)) 
    145     populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname)) 
     186    populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname)) 
    146187    return response 
    147188 
    148 def delete_object(request, model, post_delete_redirect, 
    149         object_id=None, slug=None, slug_field='slug', template_name=None, 
    150         template_loader=loader, extra_context=None, 
    151         login_required=False, context_processors=None, template_object_name='object'): 
     189def delete_object(request, model, post_delete_redirect, object_id=None, 
     190        slug=None, slug_field='slug', template_name=None, 
     191        template_loader=loader, extra_context=None, login_required=False, 
     192        context_processors=None, template_object_name='object'): 
    152193    """ 
    153194    Generic object-delete function. 
     
    166207        return redirect_to_login(request.path) 
    167208 
    168     # Look up the object to be edited 
    169     lookup_kwargs = {} 
    170     if object_id: 
    171         lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id 
    172     elif slug and slug_field: 
    173         lookup_kwargs['%s__exact' % slug_field] = slug 
    174     else: 
    175         raise AttributeError("Generic delete view must be called with either an object_id or a slug/slug_field") 
    176     try: 
    177         object = model._default_manager.get(**lookup_kwargs) 
    178     except ObjectDoesNotExist: 
    179         raise Http404, "No %s found for %s" % (model._meta.app_label, lookup_kwargs) 
     209    obj = lookup_object(model, object_id, slug, slug_field) 
    180210 
    181211    if request.method == 'POST': 
    182         object.delete() 
     212        obj.delete() 
    183213        if request.user.is_authenticated(): 
    184214            request.user.message_set.create(message=ugettext("The %(verbose_name)s was deleted.") % {"verbose_name": model._meta.verbose_name}) 
     
    189219        t = template_loader.get_template(template_name) 
    190220        c = RequestContext(request, { 
    191             template_object_name: object
     221            template_object_name: obj
    192222        }, context_processors) 
    193         for key, value in extra_context.items(): 
    194             if callable(value): 
    195                 c[key] = value() 
    196             else: 
    197                 c[key] = value 
     223        apply_extra_context(extra_context, c) 
    198224        response = HttpResponse(t.render(c)) 
    199         populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname)) 
     225        populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname)) 
    200226        return response 
  • django/trunk/django/views/generic/__init__.py

    r4265 r7952  
     1class GenericViewError(Exception): 
     2    """A problem in a generic view.""" 
     3    pass 
  • django/trunk/docs/generic_views.txt

    r7866 r7952  
    702702      the URLconf. See `Notes on pagination`_ below. 
    703703 
    704     * ``page``: The current page number, as an integer. This is 1-based.  
     704    * ``page``: The current page number, as an integer. This is 1-based. 
    705705      See `Notes on pagination`_ below. 
    706706 
     
    810810        /objects/?page=3 
    811811 
    812     * To loop over all the available page numbers, use the ``page_range``  
    813       variable. You can iterate over the list provided by ``page_range``  
     812    * To loop over all the available page numbers, use the ``page_range`` 
     813      variable. You can iterate over the list provided by ``page_range`` 
    814814      to create a link to every page of results. 
    815815 
    816816These values and lists are 1-based, not 0-based, so the first page would be 
    817 represented as page ``1``.  
     817represented as page ``1``. 
    818818 
    819819For more on pagination, read the `pagination documentation`_. 
     
    821821.. _`pagination documentation`: ../pagination/ 
    822822 
    823 **New in Django development version:**  
     823**New in Django development version:** 
    824824 
    825825As a special case, you are also permitted to use ``last`` as a value for 
     
    828828    /objects/?page=last 
    829829 
    830 This allows you to access the final page of results without first having to  
     830This allows you to access the final page of results without first having to 
    831831determine how many pages there are. 
    832832 
     
    907907for creating, editing and deleting objects. 
    908908 
     909**Changed in Django development version:** 
     910 
     911``django.views.generic.create_update.create_object`` and 
     912``django.views.generic.create_update.update_object`` now use `newforms`_ to 
     913build and display the form. 
     914 
     915.. _newforms: ../newforms/ 
     916 
    909917``django.views.generic.create_update.create_object`` 
    910918---------------------------------------------------- 
     
    913921 
    914922A page that displays a form for creating an object, redisplaying the form with 
    915 validation errors (if there are any) and saving the object. This uses the 
    916 automatic manipulators that come with Django models. 
    917  
    918 **Required arguments:** 
    919  
    920     * ``model``: The Django model class of the object that the form will 
    921       create. 
     923validation errors (if there are any) and saving the object. 
     924 
     925**Required arguments:** 
     926 
     927    * Either ``form_class`` or ``model`` is required. 
     928 
     929      If you provide ``form_class``, it should be a 
     930      ``django.newforms.ModelForm`` subclass.  Use this argument when you need 
     931      to customize the model's form.  See the `ModelForm docs`_ for more 
     932      information. 
     933 
     934      Otherwise, ``model`` should be a Django model class and the form used 
     935      will be a standard ``ModelForm`` for ``model``. 
    922936 
    923937**Optional arguments:** 
     
    960974In addition to ``extra_context``, the template's context will be: 
    961975 
    962     * ``form``: A ``django.oldforms.FormWrapper`` instance representing the form 
    963       for editing the object. This lets you refer to form fields easily in the 
     976    * ``form``: A ``django.newforms.ModelForm`` instance representing the form 
     977      for creating the object. This lets you refer to form fields easily in the 
    964978      template system. 
    965979 
    966       For example, if ``model`` has two fields, ``name`` and ``address``:: 
     980      For example, if the model has two fields, ``name`` and ``address``:: 
    967981 
    968982          <form action="" method="post"> 
    969           <p><label for="id_name">Name:</label> {{ form.name }}</p> 
    970           <p><label for="id_address">Address:</label> {{ form.address }}</p> 
     983          <p>{{ form.name.label_tag }} {{ form.name }}</p> 
     984          <p>{{ form.address.label_tag }} {{ form.address }}</p> 
    971985          </form> 
    972986 
    973       See the `manipulator and formfield documentation`_ for more information 
    974       about using ``FormWrapper`` objects in templates. 
     987      See the `newforms documentation`_ for more information about using 
     988      ``Form`` objects in templates. 
    975989 
    976990.. _authentication system: ../authentication/ 
    977 .. _manipulator and formfield documentation: ../forms/ 
     991.. _ModelForm docs: ../newforms/modelforms 
     992.. _newforms documentation: ../newforms/ 
    978993 
    979994``django.views.generic.create_update.update_object`` 
     
    9881003**Required arguments:** 
    9891004 
    990     * ``model``: The Django model class of the object that the form will 
    991       create. 
     1005    * Either ``form_class`` or ``model`` is required. 
     1006 
     1007      If you provide ``form_class``, it should be a 
     1008      ``django.newforms.ModelForm`` subclass.  Use this argument when you need 
     1009      to customize the model's form.  See the `ModelForm docs`_ for more 
     1010      information. 
     1011 
     1012      Otherwise, ``model`` should be a Django model class and the form used 
     1013      will be a standard ``ModelForm`` for ``model``. 
    9921014 
    9931015    * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. 
     
    10421064In addition to ``extra_context``, the template's context will be: 
    10431065 
    1044     * ``form``: A ``django.oldforms.FormWrapper`` instance representing the form 
     1066    * ``form``: A ``django.newforms.ModelForm`` instance representing the form 
    10451067      for editing the object. This lets you refer to form fields easily in the 
    10461068      template system. 
    10471069 
    1048       For example, if ``model`` has two fields, ``name`` and ``address``:: 
     1070      For example, if the model has two fields, ``name`` and ``address``:: 
    10491071 
    10501072          <form action="" method="post"> 
    1051           <p><label for="id_name">Name:</label> {{ form.name }}</p> 
    1052           <p><label for="id_address">Address:</label> {{ form.address }}</p> 
     1073          <p>{{ form.name.label_tag }} {{ form.name }}</p> 
     1074          <p>{{ form.address.label_tag }} {{ form.address }}</p> 
    10531075          </form> 
    10541076 
    1055       See the `manipulator and formfield documentation`_ for more information 
    1056       about using ``FormWrapper`` objects in templates. 
     1077      See the `newforms documentation`_ for more information about using 
     1078      ``Form`` objects in templates. 
    10571079 
    10581080    * ``object``: The original object being edited. This variable's name 
  • django/trunk/tests/regressiontests/views/fixtures/testdata.json

    r6374 r7952  
    11[ 
     2    { 
     3        "pk": "1", 
     4        "model": "auth.user", 
     5        "fields": { 
     6            "username": "testclient", 
     7            "first_name": "Test", 
     8            "last_name": "Client", 
     9            "is_active": true, 
     10            "is_superuser": false, 
     11            "is_staff": false, 
     12            "last_login": "2006-12-17 07:03:31", 
     13            "groups": [], 
     14            "user_permissions": [], 
     15            "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", 
     16            "email": "testclient@example.com", 
     17            "date_joined": "2006-12-17 07:03:31" 
     18        } 
     19    }, 
    220    { 
    321        "pk": 1,  
     
    3048        } 
    3149    },  
    32  
     50        { 
     51        "pk": 1, 
     52        "model": "views.urlarticle", 
     53        "fields": { 
     54            "author": 1, 
     55            "title": "Old Article", 
     56            "slug": "old_article", 
     57            "date_created": "2001-01-01 21:22:23" 
     58        } 
     59    }, 
    3360    { 
    3461        "pk": 1,  
  • django/trunk/tests/regressiontests/views/models.py

    r7294 r7952  
    11""" 
    2 Regression tests for Django built-in views 
     2Regression tests for Django built-in views. 
    33""" 
    44 
    55from django.db import models 
    6 from django.conf import settings 
    76 
    87class Author(models.Model): 
     
    1514        return '/views/authors/%s/' % self.id 
    1615 
    17  
    18 class Article(models.Model): 
     16class BaseArticle(models.Model): 
     17    """ 
     18    An abstract article Model so that we can create article models with and 
     19    without a get_absolute_url method (for create_update generic views tests). 
     20    """ 
    1921    title = models.CharField(max_length=100) 
    2022    slug = models.SlugField() 
    2123    author = models.ForeignKey(Author) 
    2224    date_created = models.DateTimeField() 
    23      
     25 
     26    class Meta: 
     27        abstract = True 
     28 
    2429    def __unicode__(self): 
    2530        return self.title 
    2631 
     32class Article(BaseArticle): 
     33    pass 
     34 
     35class UrlArticle(BaseArticle): 
     36    """ 
     37    An Article class with a get_absolute_url defined. 
     38    """ 
     39    def get_absolute_url(self): 
     40        return '/urlarticles/%s/' % self.slug 
  • django/trunk/tests/regressiontests/views/tests/__init__.py

    r7294 r7952  
    33from static import * 
    44from generic.date_based import * 
     5from generic.create_update import * 
  • django/trunk/tests/regressiontests/views/urls.py

    r7294 r7952  
    55from models import * 
    66import views 
     7 
    78 
    89base_dir = path.dirname(path.abspath(__file__)) 
     
    1516} 
    1617 
    17 date_based_info_dict = {  
    18     'queryset': Article.objects.all(),  
    19     'date_field': 'date_created',  
    20     'month_format': '%m',  
    21 }  
     18date_based_info_dict = { 
     19    'queryset': Article.objects.all(), 
     20    'date_field': 'date_created', 
     21    'month_format': '%m', 
     22} 
    2223 
    2324urlpatterns = patterns('', 
    2425    (r'^$', views.index_page), 
    25      
     26 
    2627    # Default views 
    2728    (r'^shortcut/(\d+)/(.*)/$', 'django.views.defaults.shortcut'), 
    2829    (r'^non_existing_url/', 'django.views.defaults.page_not_found'), 
    2930    (r'^server_error/', 'django.views.defaults.server_error'), 
    30      
     31 
    3132    # i18n views 
    32     (r'^i18n/', include('django.conf.urls.i18n')),     
     33    (r'^i18n/', include('django.conf.urls.i18n')), 
    3334    (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), 
    34      
     35 
    3536    # Static views 
    3637    (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': media_dir}), 
    37      
    38         # Date-based generic views 
    39     (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/$',  
    40         'django.views.generic.date_based.object_detail',  
    41         dict(slug_field='slug', **date_based_info_dict)),  
    42     (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/allow_future/$',  
    43         'django.views.generic.date_based.object_detail',  
    44         dict(allow_future=True, slug_field='slug', **date_based_info_dict)),  
    45     (r'^date_based/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$',  
    46         'django.views.generic.date_based.archive_month',  
    47         date_based_info_dict),      
    4838) 
     39 
     40# Date-based generic views. 
     41urlpatterns += patterns('django.views.generic.date_based', 
     42    (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/$', 
     43        'object_detail', 
     44        dict(slug_field='slug', **date_based_info_dict)), 
     45    (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/allow_future/$', 
     46        'object_detail', 
     47        dict(allow_future=True, slug_field='slug', **date_based_info_dict)), 
     48    (r'^date_based/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$', 
     49        'archive_month', 
     50        date_based_info_dict), 
     51) 
     52 
     53# crud generic views. 
     54 
     55urlpatterns += patterns('django.views.generic.create_update', 
     56    (r'^create_update/member/create/article/$', 'create_object', 
     57        dict(login_required=True, model=Article)), 
     58    (r'^create_update/create/article/$', 'create_object', 
     59        dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', 
     60             model=Article)), 
     61    (r'^create_update/update/article/(?P<slug>[-\w]+)/$', 'update_object', 
     62        dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', 
     63             slug_field='slug', model=Article)), 
     64    (r'^create_update/create_custom/article/$', views.custom_create), 
     65    (r'^create_update/delete/article/(?P<slug>[-\w]+)/$', 'delete_object', 
     66        dict(post_delete_redirect='/views/create_update/', slug_field='slug', 
     67             model=Article)), 
     68 
     69    # No post_save_redirect and no get_absolute_url on model. 
     70    (r'^create_update/no_redirect/create/article/$', 'create_object', 
     71        dict(model=Article)), 
     72    (r'^create_update/no_redirect/update/article/(?P<slug>[-\w]+)/$', 
     73        'update_object', dict(slug_field='slug', model=Article)), 
     74 
     75    # get_absolute_url on model, but no passed post_save_redirect. 
     76    (r'^create_update/no_url/create/article/$', 'create_object', 
     77        dict(model=UrlArticle)), 
     78    (r'^create_update/no_url/update/article/(?P<slug>[-\w]+)/$', 
     79        'update_object', dict(slug_field='slug', model=UrlArticle)), 
     80) 
  • django/trunk/tests/regressiontests/views/views.py

    r7294 r7952  
    11from django.http import HttpResponse 
     2import django.newforms as forms 
     3from django.views.generic.create_update import create_object 
     4 
     5from models import Article 
     6 
    27 
    38def index_page(request): 
    49    """Dummy index page""" 
    510    return HttpResponse('<html><body>Dummy page</body></html>') 
     11 
     12 
     13def custom_create(request): 
     14    """ 
     15    Calls create_object generic view with a custom form class. 
     16    """ 
     17    class SlugChangingArticleForm(forms.ModelForm): 
     18        """Custom form class to overwrite the slug.""" 
     19 
     20        class Meta: 
     21            model = Article 
     22 
     23        def save(self, *args, **kwargs): 
     24            self.cleaned_data['slug'] = 'some-other-slug' 
     25            return super(SlugChangingArticleForm, self).save(*args, **kwargs) 
     26 
     27    return create_object(request, 
     28        post_save_redirect='/views/create_update/view/article/%(slug)s/', 
     29        form_class=SlugChangingArticleForm) 
  • django/trunk/tests/templates/views/article_detail.html

    r7294 r7952  
    1 This template intentionally left blank 
     1Article detail template.