Ticket #3639: r6635_create_update_newforms_with_docs_and_tests.diff
File r6635_create_update_newforms_with_docs_and_tests.diff, 16.6 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, form=forms.BaseForm, 13 formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): 14 14 """ 15 15 Generic object-creation function. 16 16 … … 23 23 if login_required and not request.user.is_authenticated(): 24 24 return redirect_to_login(request.path) 25 25 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() 26 ModelForm = forms.form_for_model(model, form=form, formfield_callback=formfield_callback) 27 if request.method == 'POST': 28 form = ModelForm(request.POST, request.FILES) 29 30 if form.is_valid(): 31 new_object = form.save() 30 32 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 33 if request.user.is_authenticated(): 43 34 request.user.message_set.create(message=ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 44 35 … … 51 42 else: 52 43 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 53 44 else: 54 # No POST, so we want a brand new form without any data or errors 55 errors = {} 56 new_data = manipulator.flatten_data() 45 form = ModelForm() 57 46 58 # Create the FormWrapper, template, context, response 59 form = oldforms.FormWrapper(manipulator, new_data, errors) 47 # Create the template, context, response 60 48 if not template_name: 61 49 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 62 50 t = template_loader.get_template(template_name) … … 73 61 def update_object(request, model, object_id=None, slug=None, 74 62 slug_field='slug', template_name=None, template_loader=loader, 75 63 extra_context=None, post_save_redirect=None, 76 login_required=False, follow=None, context_processors=None, 77 template_object_name='object'): 64 login_required=False, context_processors=None, 65 template_object_name='object', form=forms.BaseForm, 66 formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): 78 67 """ 79 68 Generic object-update function. 80 69 … … 102 91 except ObjectDoesNotExist: 103 92 raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs) 104 93 105 manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)94 ModelForm = forms.form_for_instance(object, form=form, formfield_callback=formfield_callback) 106 95 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) 96 if request.method == 'POST': 97 form = ModelForm(request.POST, request.FILES) 98 if form.is_valid(): 99 object = form.save() 115 100 116 101 if request.user.is_authenticated(): 117 102 request.user.message_set.create(message=ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) … … 124 109 else: 125 110 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 126 111 else: 127 errors = {} 128 # This makes sure the form acurate represents the fields of the place. 129 new_data = manipulator.flatten_data() 112 form = ModelForm() 130 113 131 form = oldforms.FormWrapper(manipulator, new_data, errors)132 114 if not template_name: 133 115 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 134 116 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:** ``formfield_callback``: A callback 954 function to easily customize the fields and widgets displayed by the form. 955 See the `newforms documentation`_ for more information. 956 941 957 **Template name:** 942 958 943 959 If ``template_name`` isn't specified, this view will use the template … … 947 963 948 964 In addition to ``extra_context``, the template's context will be: 949 965 950 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form966 * ``form``: A ``django.newforms.Form`` instance representing the form 951 967 for editing the object. This lets you refer to form fields easily in the 952 968 template system. 953 969 954 970 For example, if ``model`` has two fields, ``name`` and ``address``:: 955 971 956 972 <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>973 <p>{{ form.name }}</p> 974 <p>{{ form.address }}</p> 959 975 </form> 960 976 961 See the ` manipulator and formfield documentation`_ for more information962 about using ``FormWrapper`` objects in templates.977 See the `newforms documentation`_ for more information about using 978 ``Form`` objects in templates. 963 979 964 980 .. _authentication system: ../authentication/ 965 .. _manipulator and formfield documentation: ../forms/ 981 .. _using an alternate base class: ../newforms/#using-an-alternate-base-class 982 .. _newforms documentation: ../newforms/ 966 983 967 984 ``django.views.generic.create_update.update_object`` 968 985 ---------------------------------------------------- … … 1019 1036 1020 1037 * ``template_object_name``: Designates the name of the template variable 1021 1038 to use in the template context. By default, this is ``'object'``. 1039 1040 * **New in Django development version:** ``form``: A ``BaseForm`` subclass 1041 to allow form customization and validation. See an example 1042 `using an alternate base class`_ to fully understand the usage of this 1043 argument. 1044 1045 * **New in Django development version:** ``formfield_callback``: A callback 1046 function to easily customize the fields and widgets displayed by the form. 1047 See the `newforms documentation`_ for more information. 1022 1048 1023 1049 **Template name:** 1024 1050 … … 1029 1055 1030 1056 In addition to ``extra_context``, the template's context will be: 1031 1057 1032 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form1058 * ``form``: A ``django.newforms.Form`` instance representing the form 1033 1059 for editing the object. This lets you refer to form fields easily in the 1034 1060 template system. 1035 1061 1036 1062 For example, if ``model`` has two fields, ``name`` and ``address``:: 1037 1063 1038 1064 <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>1065 <p>{{ form.name }}</p> 1066 <p>{{ form.address }}</p> 1041 1067 </form> 1042 1068 1043 See the ` manipulator and formfield documentation`_ for more information1044 about using ``FormWrapper`` objects in templates.1069 See the `newforms documentation`_ for more information about using 1070 ``Form`` objects in templates. 1045 1071 1046 1072 * ``object``: The original object being edited. This variable's name 1047 1073 depends on the ``template_object_name`` parameter, which is ``'object'``