Ticket #3639: r6635_create_update_newforms_with_docs_and_tests2.diff
File r6635_create_update_newforms_with_docs_and_tests2.diff, 17.2 KB (added by , 17 years ago) |
---|
-
django/views/generic/create_update.py
1 1 from django.core.xheaders import populate_xheaders 2 2 from django.template import loader 3 from django import oldforms 4 from django.db.models import FileField 3 from django import newforms as forms 5 4 from django.contrib.auth.views import redirect_to_login 6 5 from django.template import RequestContext 7 6 from django.http import Http404, HttpResponse, HttpResponseRedirect … … 10 9 11 10 def create_object(request, model, template_name=None, 12 11 template_loader=loader, extra_context=None, post_save_redirect=None, 13 login_required=False, follow=None, context_processors=None):12 login_required=False, context_processors=None, **kwargs): 14 13 """ 15 14 Generic object-creation function. 16 15 … … 23 22 if login_required and not request.user.is_authenticated(): 24 23 return redirect_to_login(request.path) 25 24 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() 25 ModelForm = forms.form_for_model(model, **kwargs) 26 if request.method == 'POST': 27 form = ModelForm(request.POST, request.FILES) 28 29 if form.is_valid(): 30 new_object = form.save() 30 31 31 if model._meta.has_field_type(FileField):32 new_data.update(request.FILES)33 34 # Check for errors35 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 42 32 if request.user.is_authenticated(): 43 33 request.user.message_set.create(message=ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 44 34 … … 51 41 else: 52 42 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 53 43 else: 54 # No POST, so we want a brand new form without any data or errors 55 errors = {} 56 new_data = manipulator.flatten_data() 44 form = ModelForm() 57 45 58 # Create the FormWrapper, template, context, response 59 form = oldforms.FormWrapper(manipulator, new_data, errors) 46 # Create the template, context, response 60 47 if not template_name: 61 48 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 62 49 t = template_loader.get_template(template_name) … … 73 60 def update_object(request, model, object_id=None, slug=None, 74 61 slug_field='slug', template_name=None, template_loader=loader, 75 62 extra_context=None, post_save_redirect=None, 76 login_required=False, follow=None,context_processors=None,77 template_object_name='object' ):63 login_required=False, context_processors=None, 64 template_object_name='object', **kwargs): 78 65 """ 79 66 Generic object-update function. 80 67 … … 102 89 except ObjectDoesNotExist: 103 90 raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs) 104 91 105 manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)92 ModelForm = forms.form_for_instance(object, **kwargs) 106 93 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) 94 if request.method == 'POST': 95 form = ModelForm(request.POST, request.FILES) 96 if form.is_valid(): 97 object = form.save() 115 98 116 99 if request.user.is_authenticated(): 117 100 request.user.message_set.create(message=ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) … … 124 107 else: 125 108 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 126 109 else: 127 errors = {} 128 # This makes sure the form acurate represents the fields of the place. 129 new_data = manipulator.flatten_data() 110 form = ModelForm() 130 111 131 form = oldforms.FormWrapper(manipulator, new_data, errors)132 112 if not template_name: 133 113 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 134 114 t = template_loader.get_template(template_name) -
tests/regressiontests/views/tests/__init__.py
1 1 from defaults import * 2 2 from i18n import * 3 3 from static import * 4 from generic.date_based import * 5 No newline at end of file 4 from generic.date_based import * 5 from generic.create_update import * 6 No newline at end of file -
tests/regressiontests/views/tests/generic/create_update.py
1 2 import datetime 3 from django.test import TestCase 4 from django.contrib.auth.views import redirect_to_login 5 from regressiontests.views.models import Article, Author 6 7 class CreateObjectTest(TestCase): 8 fixtures = ['testdata.json'] 9 10 def test_not_logged_in(self): 11 """Verifies the user is logged in through the login_required kwarg""" 12 view_url = '/views/create_update/member/create/article/' 13 response = self.client.get(view_url) 14 self.assertRedirects(response, 'http://testserver/accounts/login/?next=%s' % view_url) 15 16 def test_create_article_display_page(self): 17 """Ensures the generic view returned the page and contains a form.""" 18 view_url = '/views/create_update/create/article/' 19 response = self.client.get(view_url) 20 self.assertEqual(response.status_code, 200) 21 if not response.context.get('form'): 22 self.fail('No form found in the response.') 23 24 def test_create_article_with_errors(self): 25 """POSTs a form that contain validation errors.""" 26 view_url = '/views/create_update/create/article/' 27 response = self.client.post(view_url, { 28 'title': 'My First Article', 29 }) 30 self.assertFormError(response, 'form', 'slug', [u'This field is required.']) 31 32 def test_create_article(self): 33 """Creates a new article through the generic view and ensures it gets 34 redirected to the correct URL defined by post_save_redirect""" 35 view_url = '/views/create_update/create/article/' 36 response = self.client.post(view_url, { 37 'title': 'My First Article', 38 'slug': 'my-first-article', 39 'author': '1', 40 'date_created': datetime.datetime(2007, 6, 25), 41 }) 42 self.assertRedirects(response, 43 'http://testserver/views/create_update/view/article/my-first-article/', 44 target_status_code=404) 45 46 class UpdateDeleteObjectTest(TestCase): 47 fixtures = ['testdata.json'] 48 49 def test_update_object_form_display(self): 50 """Verifies that the form was created properly and with initial values.""" 51 response = self.client.get('/views/create_update/update/article/old_article/') 52 self.assertEquals(unicode(response.context['form']['title']), 53 u'<input id="id_title" type="text" name="title" value="Old Article" maxlength="100" />') 54 55 def test_update_object(self): 56 """Verifies the form POST data and performs a redirect to 57 post_save_redirect""" 58 response = self.client.post('/views/create_update/update/article/old_article/', { 59 'title': 'Another Article', 60 'slug': 'another-article-slug', 61 'author': 1, 62 'date_created': datetime.datetime(2007, 6, 25), 63 }) 64 self.assertRedirects(response, 65 'http://testserver/views/create_update/view/article/another-article-slug/', 66 target_status_code=404) 67 article = Article.objects.get(pk=1) 68 self.assertEquals(article.title, "Another Article") 69 70 def test_delete_object_confirm(self): 71 """Verifies the confirm deletion page is displayed using a GET.""" 72 response = self.client.get('/views/create_update/delete/article/old_article/') 73 self.assertTemplateUsed(response, 'views/article_confirm_delete.html') 74 75 def test_delete_object_redirect(self): 76 """Verifies that post_delete_redirect works properly.""" 77 response = self.client.post('/views/create_update/delete/article/old_article/') 78 self.assertRedirects(response, 79 'http://testserver/views/create_update/', 80 target_status_code=404) 81 82 def test_delete_object(self): 83 """Verifies the object actually gets deleted on a POST.""" 84 response = self.client.post('/views/create_update/delete/article/old_article/') 85 try: 86 Article.objects.get(slug='old_article') 87 except Article.DoesNotExist: 88 pass 89 else: 90 self.fail('Object was not deleted.') 91 92 No newline at end of file -
tests/regressiontests/views/urls.py
20 20 'month_format': '%m', 21 21 } 22 22 23 crud_info_dict = { 24 'model': Article, 25 } 26 23 27 urlpatterns = patterns('', 24 28 (r'^$', views.index_page), 25 29 … … 44 48 dict(allow_future=True, slug_field='slug', **date_based_info_dict)), 45 49 (r'^date_based/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$', 46 50 'django.views.generic.date_based.archive_month', 47 date_based_info_dict), 51 date_based_info_dict), 52 53 # crud generic views 54 (r'^create_update/member/create/article/$', 'django.views.generic.create_update.create_object', 55 dict(login_required=True, **crud_info_dict)), 56 (r'^create_update/create/article/$', 'django.views.generic.create_update.create_object', 57 dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', **crud_info_dict)), 58 (r'create_update/update/article/(?P<slug>[-\w]+)/$', 'django.views.generic.create_update.update_object', 59 dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', slug_field='slug', **crud_info_dict)), 60 (r'create_update/delete/article/(?P<slug>[-\w]+)/$', 'django.views.generic.create_update.delete_object', 61 dict(post_delete_redirect='/views/create_update/', slug_field='slug', **crud_info_dict)), 48 62 ) -
tests/templates/views/article_confirm_delete.html
1 This template intentionally left blank 2 No newline at end of file -
tests/templates/views/article_form.html
1 This template intentionally left blank 2 No newline at end of file -
docs/generic_views.txt
894 894 The ``django.views.generic.create_update`` module contains a set of functions 895 895 for creating, editing and deleting objects. 896 896 897 **New in Django development version:** 898 899 ``django.views.generic.create_update.create_object`` and 900 ``django.views.generic.create_update.update_object`` now use `newforms`_ to 901 build and display the form. 902 903 .. _newforms: ../newforms/ 904 897 905 ``django.views.generic.create_update.create_object`` 898 906 ---------------------------------------------------- 899 907 900 908 **Description:** 901 909 902 910 A page that displays a form for creating an object, redisplaying the form with 903 validation errors (if there are any) and saving the object. This uses the 904 automatic manipulators that come with Django models. 911 validation errors (if there are any) and saving the object. 905 912 906 913 **Required arguments:** 907 914 … … 938 945 * ``context_processors``: A list of template-context processors to apply to 939 946 the view's template. See the `RequestContext docs`_. 940 947 948 * **New in Django development version:** ``form``: A ``BaseForm`` subclass 949 to allow form customization and validation. See an example 950 `using an alternate base class`_ to fully understand the usage of this 951 argument. 952 953 * **New in Django development version:** ``fields``: A list of fields to 954 include in the ``Form`` object. See an example `using a subset of fields`_ 955 for more information. 956 957 * **New in Django development version:** ``formfield_callback``: A callback 958 function to easily customize the default fields and widgets displayed 959 by the form. See an example `how to override default field types`_ for 960 more information. 961 941 962 **Template name:** 942 963 943 964 If ``template_name`` isn't specified, this view will use the template … … 947 968 948 969 In addition to ``extra_context``, the template's context will be: 949 970 950 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form971 * ``form``: A ``django.newforms.Form`` instance representing the form 951 972 for editing the object. This lets you refer to form fields easily in the 952 973 template system. 953 974 954 975 For example, if ``model`` has two fields, ``name`` and ``address``:: 955 976 956 977 <form action="" method="post"> 957 <p> <label for="id_name">Name:</label>{{ form.name }}</p>958 <p> <label for="id_address">Address:</label>{{ form.address }}</p>978 <p>{{ form.name.label_tag }} {{ form.name }}</p> 979 <p>{{ form.address.label_tag }} {{ form.address }}</p> 959 980 </form> 960 981 961 See the ` manipulator and formfield documentation`_ for more information962 about using ``FormWrapper`` objects in templates.982 See the `newforms documentation`_ for more information about using 983 ``Form`` objects in templates. 963 984 964 985 .. _authentication system: ../authentication/ 965 .. _manipulator and formfield documentation: ../forms/ 986 .. _using an alternate base class: ../newforms/#using-an-alternate-base-class 987 .. _using a subset of fields: ../newforms/#using-a-subset-of-fields-on-the-form 988 .. _how to override default field types: ../newforms/#overriding-the-default-field-types 989 .. _newforms documentation: ../newforms/ 966 990 967 991 ``django.views.generic.create_update.update_object`` 968 992 ---------------------------------------------------- … … 1019 1043 1020 1044 * ``template_object_name``: Designates the name of the template variable 1021 1045 to use in the template context. By default, this is ``'object'``. 1046 1047 * **New in Django development version:** ``form``: A ``BaseForm`` subclass 1048 to allow form customization and validation. See an example 1049 `using an alternate base class`_ to fully understand the usage of this 1050 argument. 1051 1052 * **New in Django development version:** ``fields``: A list of fields to 1053 include in the ``Form`` object. See an example `using a subset of fields`_ 1054 for more information. 1055 1056 * **New in Django development version:** ``formfield_callback``: A callback 1057 function to easily customize the default fields and widgets displayed 1058 by the form. See an example `how to override default field types`_ for 1059 more information. 1022 1060 1023 1061 **Template name:** 1024 1062 … … 1029 1067 1030 1068 In addition to ``extra_context``, the template's context will be: 1031 1069 1032 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form1070 * ``form``: A ``django.newforms.Form`` instance representing the form 1033 1071 for editing the object. This lets you refer to form fields easily in the 1034 1072 template system. 1035 1073 1036 1074 For example, if ``model`` has two fields, ``name`` and ``address``:: 1037 1075 1038 1076 <form action="" method="post"> 1039 <p> <label for="id_name">Name:</label>{{ form.name }}</p>1040 <p> <label for="id_address">Address:</label>{{ form.address }}</p>1077 <p>{{ form.name.label_tag }} {{ form.name }}</p> 1078 <p>{{ form.address.label_tag }} {{ form.address }}</p> 1041 1079 </form> 1080 1081 See the `newforms documentation`_ for more information about using 1082 ``Form`` objects in templates. 1042 1083 1043 See the `manipulator and formfield documentation`_ for more information1044 about using ``FormWrapper`` objects in templates.1045 1046 1084 * ``object``: The original object being edited. This variable's name 1047 1085 depends on the ``template_object_name`` parameter, which is ``'object'`` 1048 1086 by default. If ``template_object_name`` is ``'foo'``, this variable's