Ticket #3639: r6635_create_update_newforms3.diff
File r6635_create_update_newforms3.diff, 19.7 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, 13 save_callback=lambda form, request: form.save(), **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() 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 26 ModelForm = forms.form_for_model(model, **kwargs) 27 28 if request.method == 'POST': 29 form = ModelForm(request.POST, request.FILES) 30 31 if form.is_valid(): 32 new_object = save_callback(form, request) 33 42 34 if request.user.is_authenticated(): 43 35 request.user.message_set.create(message=ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 44 36 … … 51 43 else: 52 44 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 53 45 else: 54 # No POST, so we want a brand new form without any data or errors 55 errors = {} 56 new_data = manipulator.flatten_data() 46 form = ModelForm() 57 47 58 # Create the FormWrapper, template, context, response 59 form = oldforms.FormWrapper(manipulator, new_data, errors) 48 # Create the template, context, response 60 49 if not template_name: 61 50 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 62 51 t = template_loader.get_template(template_name) … … 73 62 def update_object(request, model, object_id=None, slug=None, 74 63 slug_field='slug', template_name=None, template_loader=loader, 75 64 extra_context=None, post_save_redirect=None, 76 login_required=False, follow=None, context_processors=None, 77 template_object_name='object'): 65 login_required=False, context_processors=None, 66 template_object_name='object', 67 save_callback=lambda form, request: form.save(), **kwargs): 78 68 """ 79 69 Generic object-update function. 80 70 … … 102 92 except ObjectDoesNotExist: 103 93 raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs) 104 94 105 manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)95 ModelForm = forms.form_for_instance(object, **kwargs) 106 96 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) 97 if request.method == 'POST': 98 form = ModelForm(request.POST, request.FILES) 99 if form.is_valid(): 100 object = save_callback(form, request) 115 101 116 102 if request.user.is_authenticated(): 117 103 request.user.message_set.create(message=ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) … … 124 110 else: 125 111 raise ImproperlyConfigured("No URL to redirect to from generic create view.") 126 112 else: 127 errors = {} 128 # This makes sure the form acurate represents the fields of the place. 129 new_data = manipulator.flatten_data() 113 form = ModelForm() 130 114 131 form = oldforms.FormWrapper(manipulator, new_data, errors)132 115 if not template_name: 133 116 template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower()) 134 117 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 def test_create_custom_save_article(self): 47 """Creates a new article with a custom save_callback to adjust the slug 48 before committing to the database.""" 49 view_url = '/views/create_update/create_custom/article/' 50 response = self.client.post(view_url, { 51 'title': 'Test Article', 52 }) 53 self.assertRedirects(response, 54 'http://testserver/views/create_update/view/article/some-other-slug/', 55 target_status_code=404) 56 57 class UpdateDeleteObjectTest(TestCase): 58 fixtures = ['testdata.json'] 59 60 def test_update_object_form_display(self): 61 """Verifies that the form was created properly and with initial values.""" 62 response = self.client.get('/views/create_update/update/article/old_article/') 63 self.assertEquals(unicode(response.context['form']['title']), 64 u'<input id="id_title" type="text" name="title" value="Old Article" maxlength="100" />') 65 66 def test_update_object(self): 67 """Verifies the form POST data and performs a redirect to 68 post_save_redirect""" 69 response = self.client.post('/views/create_update/update/article/old_article/', { 70 'title': 'Another Article', 71 'slug': 'another-article-slug', 72 'author': 1, 73 'date_created': datetime.datetime(2007, 6, 25), 74 }) 75 self.assertRedirects(response, 76 'http://testserver/views/create_update/view/article/another-article-slug/', 77 target_status_code=404) 78 article = Article.objects.get(pk=1) 79 self.assertEquals(article.title, "Another Article") 80 81 def test_delete_object_confirm(self): 82 """Verifies the confirm deletion page is displayed using a GET.""" 83 response = self.client.get('/views/create_update/delete/article/old_article/') 84 self.assertTemplateUsed(response, 'views/article_confirm_delete.html') 85 86 def test_delete_object_redirect(self): 87 """Verifies that post_delete_redirect works properly.""" 88 response = self.client.post('/views/create_update/delete/article/old_article/') 89 self.assertRedirects(response, 90 'http://testserver/views/create_update/', 91 target_status_code=404) 92 93 def test_delete_object(self): 94 """Verifies the object actually gets deleted on a POST.""" 95 response = self.client.post('/views/create_update/delete/article/old_article/') 96 try: 97 Article.objects.get(slug='old_article') 98 except Article.DoesNotExist: 99 pass 100 else: 101 self.fail('Object was not deleted.') 102 103 No newline at end of file -
tests/regressiontests/views/views.py
1 import datetime 1 2 from django.http import HttpResponse 2 3 from django.template import RequestContext 4 from django.views.generic.create_update import create_object 3 5 6 from models import Author 7 4 8 def index_page(request): 5 9 """Dummy index page""" 6 10 return HttpResponse('<html><body>Dummy page</body></html>') 7 11 12 def custom_slug(request, **kwargs): 13 def mysave(form, request): 14 instance = form.save(commit=False) 15 instance.slug = 'some-other-slug' 16 instance.author = Author.objects.get(pk=1) 17 # this would normally be done through the default value of the field 18 # this is just here to prove that it can be done in a save_callback 19 instance.date_created = datetime.datetime.now() 20 instance.save() 21 return instance 22 return create_object(request, save_callback=mysave, 23 fields=('title',), **kwargs) 24 -
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/create_custom/article/$', views.custom_slug, 59 dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', **crud_info_dict)), 60 (r'create_update/update/article/(?P<slug>[-\w]+)/$', 'django.views.generic.create_update.update_object', 61 dict(post_save_redirect='/views/create_update/view/article/%(slug)s/', slug_field='slug', **crud_info_dict)), 62 (r'create_update/delete/article/(?P<slug>[-\w]+)/$', 'django.views.generic.create_update.delete_object', 63 dict(post_delete_redirect='/views/create_update/', slug_field='slug', **crud_info_dict)), 48 64 ) -
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:** ``save_callback``: A callback 949 function to override how the form gets saved to the database. 950 951 The ``save_callback`` function will be given a ``Form`` instance with 952 the cleaned data and the ``HttpRequest`` instance passed to the view. 953 954 * **New in Django development version:** ``form``: A ``BaseForm`` subclass 955 to allow form customization and validation. See an example 956 `using an alternate base class`_ to fully understand the usage of this 957 argument. 958 959 * **New in Django development version:** ``fields``: A list of fields to 960 include in the ``Form`` object. See an example `using a subset of fields`_ 961 for more information. 962 963 * **New in Django development version:** ``formfield_callback``: A callback 964 function to easily customize the default fields and widgets displayed 965 by the form. See an example `how to override default field types`_ for 966 more information. 967 941 968 **Template name:** 942 969 943 970 If ``template_name`` isn't specified, this view will use the template … … 947 974 948 975 In addition to ``extra_context``, the template's context will be: 949 976 950 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form977 * ``form``: A ``django.newforms.Form`` instance representing the form 951 978 for editing the object. This lets you refer to form fields easily in the 952 979 template system. 953 980 954 981 For example, if ``model`` has two fields, ``name`` and ``address``:: 955 982 956 983 <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>984 <p>{{ form.name.label_tag }} {{ form.name }}</p> 985 <p>{{ form.address.label_tag }} {{ form.address }}</p> 959 986 </form> 960 987 961 See the ` manipulator and formfield documentation`_ for more information962 about using ``FormWrapper`` objects in templates.988 See the `newforms documentation`_ for more information about using 989 ``Form`` objects in templates. 963 990 964 991 .. _authentication system: ../authentication/ 965 .. _manipulator and formfield documentation: ../forms/ 992 .. _using an alternate base class: ../newforms/#using-an-alternate-base-class 993 .. _using a subset of fields: ../newforms/#using-a-subset-of-fields-on-the-form 994 .. _how to override default field types: ../newforms/#overriding-the-default-field-types 995 .. _newforms documentation: ../newforms/ 966 996 967 997 ``django.views.generic.create_update.update_object`` 968 998 ---------------------------------------------------- … … 1019 1049 1020 1050 * ``template_object_name``: Designates the name of the template variable 1021 1051 to use in the template context. By default, this is ``'object'``. 1052 1053 * **New in Django development version:** ``save_callback``: A callback 1054 function to override how the form gets saved to the database. 1055 1056 The ``save_callback`` function will be given a ``Form`` instance with 1057 the cleaned data and the ``HttpRequest`` instance passed to the view. 1058 1059 * **New in Django development version:** ``form``: A ``BaseForm`` subclass 1060 to allow form customization and validation. See an example 1061 `using an alternate base class`_ to fully understand the usage of this 1062 argument. 1063 1064 * **New in Django development version:** ``fields``: A list of fields to 1065 include in the ``Form`` object. See an example `using a subset of fields`_ 1066 for more information. 1067 1068 * **New in Django development version:** ``formfield_callback``: A callback 1069 function to easily customize the default fields and widgets displayed 1070 by the form. See an example `how to override default field types`_ for 1071 more information. 1022 1072 1023 1073 **Template name:** 1024 1074 … … 1029 1079 1030 1080 In addition to ``extra_context``, the template's context will be: 1031 1081 1032 * ``form``: A ``django. oldforms.FormWrapper`` instance representing the form1082 * ``form``: A ``django.newforms.Form`` instance representing the form 1033 1083 for editing the object. This lets you refer to form fields easily in the 1034 1084 template system. 1035 1085 1036 1086 For example, if ``model`` has two fields, ``name`` and ``address``:: 1037 1087 1038 1088 <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>1089 <p>{{ form.name.label_tag }} {{ form.name }}</p> 1090 <p>{{ form.address.label_tag }} {{ form.address }}</p> 1041 1091 </form> 1092 1093 See the `newforms documentation`_ for more information about using 1094 ``Form`` objects in templates. 1042 1095 1043 See the `manipulator and formfield documentation`_ for more information1044 about using ``FormWrapper`` objects in templates.1045 1046 1096 * ``object``: The original object being edited. This variable's name 1047 1097 depends on the ``template_object_name`` parameter, which is ``'object'`` 1048 1098 by default. If ``template_object_name`` is ``'foo'``, this variable's