Code

Ticket #4236: create_update_newforms.diff

File create_update_newforms.diff, 13.9 KB (added by dmach@…, 7 years ago)
Line 
1Index: django/views/generic/create_update.py
2===================================================================
3--- django/views/generic/create_update.py       (revision 5155)
4+++ django/views/generic/create_update.py       (working copy)
5@@ -1,16 +1,19 @@
6+from django import newforms as forms
7+from django.contrib.auth.views import redirect_to_login
8+from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
9 from django.core.xheaders import populate_xheaders
10-from django.template import loader
11-from django import oldforms
12 from django.db.models import FileField
13-from django.contrib.auth.views import redirect_to_login
14-from django.template import RequestContext
15 from django.http import Http404, HttpResponse, HttpResponseRedirect
16-from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
17 from django.utils.translation import gettext
18+from django.template import RequestContext, loader
19 
20-def create_object(request, model, template_name=None,
21-        template_loader=loader, extra_context=None, post_save_redirect=None,
22-        login_required=False, follow=None, context_processors=None):
23+
24+def create_object(request, model,
25+        template_name=None, template_loader=loader,
26+        extra_context=None, post_save_redirect=None,
27+        login_required=False, context_processors=None,
28+        not_required_fields=None, hidden_fields=None,
29+        default_data=None, choices=None):
30     """
31     Generic object-creation function.
32 
33@@ -19,62 +22,93 @@
34         form
35             the form wrapper for the object
36     """
37-    if extra_context is None: extra_context = {}
38     if login_required and not request.user.is_authenticated():
39         return redirect_to_login(request.path)
40 
41-    manipulator = model.AddManipulator(follow=follow)
42-    if request.POST:
43-        # If data was POSTed, we're trying to create a new object
44+    FormClass = forms.models.form_for_model(model)
45+    if request.method == 'POST':
46+        # Create a form with POSTed data
47         new_data = request.POST.copy()
48 
49         if model._meta.has_field_type(FileField):
50             new_data.update(request.FILES)
51 
52-        # Check for errors
53-        errors = manipulator.get_validation_errors(new_data)
54-        manipulator.do_html2python(new_data)
55+        form = FormClass(new_data)
56 
57-        if not errors:
58-            # No errors -- this means we can save the data!
59-            new_object = manipulator.save(new_data)
60+    else:
61+        # Create new form with default data
62+        if default_data is None: default_data = {}
63+        form = FormClass(default_data)
64 
65-            if request.user.is_authenticated():
66-                request.user.message_set.create(message=gettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name})
67+    # Modify choices
68+    if choices:
69+        for field in choices.keys():
70+            if not form.fields.has_key(field): raise FieldDoesNotExist
71+            form.fields[field] = forms.ChoiceField(choices[field])
72 
73-            # Redirect to the new object: first by trying post_save_redirect,
74-            # then by obj.get_absolute_url; fail if neither works.
75-            if post_save_redirect:
76-                return HttpResponseRedirect(post_save_redirect % new_object.__dict__)
77-            elif hasattr(new_object, 'get_absolute_url'):
78-                return HttpResponseRedirect(new_object.get_absolute_url())
79-            else:
80-                raise ImproperlyConfigured("No URL to redirect to from generic create view.")
81-    else:
82-        # No POST, so we want a brand new form without any data or errors
83-        errors = {}
84-        new_data = manipulator.flatten_data()
85+    # Mark some fields as 'hidden'
86+    if hidden_fields:
87+        for (field, value) in hidden_fields.iteritems():
88+            if not form.fields.has_key(field): raise FieldDoesNotExist
89+            form.fields[field].widget = forms.HiddenInput()
90+            # Prevent hidden field from being modified
91+            form.data[field] = value
92+            # Empty hidden fields are supposed not to be required
93+            if value == "": form.fields[field].required = False
94 
95-    # Create the FormWrapper, template, context, response
96-    form = oldforms.FormWrapper(manipulator, new_data, errors)
97-    if not template_name:
98+    # Mark some fields as 'not required'
99+    if not_required_fields:
100+        for field in not_required_fields:
101+            if not form.fields.has_key(field): raise FieldDoesNotExist
102+            form.fields[field].required = False
103+
104+    if request.method == 'POST' and not form.errors:
105+        # Form does't contain errors, save the data
106+        new_object = form.save()
107+
108+        if request.user.is_authenticated():
109+            request.user.message_set.create(message=gettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name})
110+
111+        # Redirect to the new object: first by trying post_save_redirect,
112+        # then by obj.get_absolute_url; fail if neither works.
113+        if post_save_redirect:
114+            return HttpResponseRedirect(post_save_redirect % new_object.__dict__)
115+        elif hasattr(new_object, 'get_absolute_url'):
116+            return HttpResponseRedirect(new_object.get_absolute_url())
117+        else:
118+            raise ImproperlyConfigured("No URL to redirect to from generic create view.")
119+
120+    # Create template
121+    if template_name is None:
122         template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
123     t = template_loader.get_template(template_name)
124+
125+    # Create context
126     c = RequestContext(request, {
127         'form': form,
128     }, context_processors)
129-    for key, value in extra_context.items():
130-        if callable(value):
131-            c[key] = value()
132-        else:
133-            c[key] = value
134+
135+    # Add extra context
136+    if extra_context:
137+        for key, value in extra_context.items():
138+            if callable(value):
139+                c[key] = value()
140+            else:
141+                c[key] = value
142+
143+    # Render template and return the response
144     return HttpResponse(t.render(c))
145 
146-def update_object(request, model, object_id=None, slug=None,
147-        slug_field=None, template_name=None, template_loader=loader,
148-        extra_context=None, post_save_redirect=None,
149-        login_required=False, follow=None, context_processors=None,
150-        template_object_name='object'):
151+
152+def update_object(request, model,
153+        object_id=None, slug=None, slug_field=None,
154+        template_name=None, template_loader=loader,
155+        template_object_name='object',
156+        extra_context=None, context_processors=None,
157+        post_save_redirect=None, login_required=False,
158+        not_required_fields=None, hidden_fields=None,
159+        choices=None):
160+
161     """
162     Generic object-update function.
163 
164@@ -85,7 +119,6 @@
165         object
166             the original object being edited
167     """
168-    if extra_context is None: extra_context = {}
169     if login_required and not request.user.is_authenticated():
170         return redirect_to_login(request.path)
171 
172@@ -97,58 +130,92 @@
173         lookup_kwargs['%s__exact' % slug_field] = slug
174     else:
175         raise AttributeError("Generic edit view must be called with either an object_id or a slug/slug_field")
176+
177     try:
178         object = model.objects.get(**lookup_kwargs)
179     except ObjectDoesNotExist:
180         raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
181 
182-    manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)
183+    FormClass = forms.models.form_for_instance(object)
184+    if request.method == 'POST':
185+        # Create a form with POSTed data
186+        new_data = request.POST.copy()
187 
188-    if request.POST:
189-        new_data = request.POST.copy()
190         if model._meta.has_field_type(FileField):
191             new_data.update(request.FILES)
192-        errors = manipulator.get_validation_errors(new_data)
193-        manipulator.do_html2python(new_data)
194-        if not errors:
195-            object = manipulator.save(new_data)
196 
197-            if request.user.is_authenticated():
198-                request.user.message_set.create(message=gettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name})
199+        form = FormClass(new_data)
200 
201-            # Do a post-after-redirect so that reload works, etc.
202-            if post_save_redirect:
203-                return HttpResponseRedirect(post_save_redirect % object.__dict__)
204-            elif hasattr(object, 'get_absolute_url'):
205-                return HttpResponseRedirect(object.get_absolute_url())
206-            else:
207-                raise ImproperlyConfigured("No URL to redirect to from generic create view.")
208     else:
209-        errors = {}
210-        # This makes sure the form acurate represents the fields of the place.
211-        new_data = manipulator.flatten_data()
212+        # Create a form with original object data
213+        form = FormClass()
214 
215-    form = oldforms.FormWrapper(manipulator, new_data, errors)
216+    # Modify choices
217+    if choices:
218+        for field in choices.keys():
219+            if not form.fields.has_key(field): raise FieldDoesNotExist
220+            form.fields[field] = forms.ChoiceField(choices[field])
221+
222+    # Mark some fields as 'hidden'
223+    if hidden_fields:
224+        for (field, value) in hidden_fields.iteritems():
225+            if not form.fields.has_key(field): raise FieldDoesNotExist
226+            form.fields[field].widget = forms.HiddenInput()
227+            # Prevent hidden field from being modified
228+            form.data[field] = value
229+            # Empty hidden fields are supposed not to be required
230+            if value == "": form.fields[field].required = False
231+
232+    # Mark some fields as 'not required'
233+    if not_required_fields:
234+        for field in not_required_fields:
235+            if not form.fields.has_key(field): raise FieldDoesNotExist
236+            form.fields[field].required = False
237+
238+    if request.method == 'POST' and not form.errors:
239+        object = form.save(new_data)
240+
241+        if request.user.is_authenticated():
242+            request.user.message_set.create(message=gettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name})
243+
244+        # Do a post-after-redirect so that reload works, etc.
245+        if post_save_redirect:
246+            return HttpResponseRedirect(post_save_redirect % object.__dict__)
247+        elif hasattr(object, 'get_absolute_url'):
248+            return HttpResponseRedirect(object.get_absolute_url())
249+        else:
250+            raise ImproperlyConfigured("No URL to redirect to from generic create view.")
251+
252+    # Create template
253     if not template_name:
254         template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
255     t = template_loader.get_template(template_name)
256+
257+    # Create context
258     c = RequestContext(request, {
259         'form': form,
260         template_object_name: object,
261     }, context_processors)
262-    for key, value in extra_context.items():
263-        if callable(value):
264-            c[key] = value()
265-        else:
266-            c[key] = value
267+
268+    # Add extra context
269+    if extra_context:
270+        for key, value in extra_context.items():
271+            if callable(value):
272+                c[key] = value()
273+            else:
274+                c[key] = value
275+
276+    # Render template and return the response
277     response = HttpResponse(t.render(c))
278     populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
279     return response
280 
281+
282 def delete_object(request, model, post_delete_redirect,
283-        object_id=None, slug=None, slug_field=None, template_name=None,
284-        template_loader=loader, extra_context=None,
285-        login_required=False, context_processors=None, template_object_name='object'):
286+        object_id=None, slug=None, slug_field=None,
287+        template_name=None, template_loader=loader,
288+        template_object_name='object', login_required=False,
289+        extra_context=None, context_processors=None):
290     """
291     Generic object-delete function.
292 
293@@ -161,7 +228,6 @@
294         object
295             the original object being deleted
296     """
297-    if extra_context is None: extra_context = {}
298     if login_required and not request.user.is_authenticated():
299         return redirect_to_login(request.path)
300 
301@@ -173,6 +239,7 @@
302         lookup_kwargs['%s__exact' % slug_field] = slug
303     else:
304         raise AttributeError("Generic delete view must be called with either an object_id or a slug/slug_field")
305+
306     try:
307         object = model._default_manager.get(**lookup_kwargs)
308     except ObjectDoesNotExist:
309@@ -183,18 +250,27 @@
310         if request.user.is_authenticated():
311             request.user.message_set.create(message=gettext("The %(verbose_name)s was deleted.") % {"verbose_name": model._meta.verbose_name})
312         return HttpResponseRedirect(post_delete_redirect)
313+
314     else:
315+        # Create template
316         if not template_name:
317-            template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
318+            template_name = "%s/%s_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
319         t = template_loader.get_template(template_name)
320+
321+        # Create context
322         c = RequestContext(request, {
323             template_object_name: object,
324         }, context_processors)
325-        for key, value in extra_context.items():
326-            if callable(value):
327-                c[key] = value()
328-            else:
329-                c[key] = value
330+
331+        # Add extra context
332+        if extra_context:
333+            for key, value in extra_context.items():
334+                if callable(value):
335+                    c[key] = value()
336+                else:
337+                    c[key] = value
338+
339+        # Render template and return the response
340         response = HttpResponse(t.render(c))
341         populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
342         return response