Author: Honza_Kral
Date: 2009-06-01 10:39:46 -0500 (Mon, 01 Jun 2009)
New Revision: 10871

Modified:
   
django/branches/soc2009/model-validation/django/contrib/contenttypes/generic.py
   django/branches/soc2009/model-validation/django/db/models/base.py
   django/branches/soc2009/model-validation/django/forms/models.py
   django/branches/soc2009/model-validation/tests/regressiontests/views/views.py
Log:
[soc2009/model-validation] Split save_instance into make and save_made

had to add a magical attribute __adding on ModelForm to indicate whether
we are editting or adding a model. This will be later passed into the
model's .clean() method since it is necessary for correctly validating
uniques (see it's current use) and can also be used to define
force_insert or force_update params to model.save()

Also beginning with this commit, .instance attribute is present on ModelForm
and should be used to do any post-processing since manipulation of the
cleaned_data will not work

Modified: 
django/branches/soc2009/model-validation/django/contrib/contenttypes/generic.py
===================================================================
--- 
django/branches/soc2009/model-validation/django/contrib/contenttypes/generic.py 
    2009-06-01 15:39:21 UTC (rev 10870)
+++ 
django/branches/soc2009/model-validation/django/contrib/contenttypes/generic.py 
    2009-06-01 15:39:46 UTC (rev 10871)
@@ -296,7 +296,11 @@
 
     def __init__(self, data=None, files=None, instance=None, save_as_new=None, 
prefix=None):
         opts = self.model._meta
-        self.instance = instance
+        if instance is None:
+            self.instance = self.model()
+        else:
+            self.instance = instance
+        self.save_as_new = save_as_new
         self.rel_name = '-'.join((
             opts.app_label, opts.object_name.lower(),
             self.ct_field.name, self.ct_fk_field.name,
@@ -324,16 +328,21 @@
             self.ct_fk_field.name: self.instance.pk,
         })
 
-    def save_new(self, form, commit=True):
+    def _construct_form(self, i, **kwargs):
         # Avoid a circular import.
         from django.contrib.contenttypes.models import ContentType
-        kwargs = {
-            self.ct_field.get_attname(): 
ContentType.objects.get_for_model(self.instance).pk,
-            self.ct_fk_field.get_attname(): self.instance.pk,
-        }
-        new_obj = self.model(**kwargs)
-        return save_instance(form, new_obj, commit=commit)
+        form = super(BaseGenericInlineFormSet, self)._construct_form(i, 
**kwargs)
+        if self.save_as_new:
+            # Remove the key from the form's data, we are only
+            # creating new instances
+            form.data[form.add_prefix(self.ct_fk_field.name)] = None
+            form.data[form.add_prefix(self.ct_field.name)] = None
 
+        # set the GenericFK value here so that the form can do it's validation
+        setattr(form.instance, self.ct_fk_field.attname, self.instance.pk)
+        setattr(form.instance, self.ct_field.attname, 
ContentType.objects.get_for_model(self.instance).pk)
+        return form
+
 def generic_inlineformset_factory(model, form=ModelForm,
                                   formset=BaseGenericInlineFormSet,
                                   ct_field="content_type", 
fk_field="object_id",

Modified: django/branches/soc2009/model-validation/django/db/models/base.py
===================================================================
--- django/branches/soc2009/model-validation/django/db/models/base.py   
2009-06-01 15:39:21 UTC (rev 10870)
+++ django/branches/soc2009/model-validation/django/db/models/base.py   
2009-06-01 15:39:46 UTC (rev 10871)
@@ -622,7 +622,14 @@
             # TODO: run this only if not errors??
             self.validate()
         except ValidationError, e:
-            errors[NON_FIELD_ERRORS] = e.messages
+            if hasattr(e, 'message_dict'):
+                if errors:
+                    for k, v in e.message_dict.items():
+                        errors.set_default(k, []).extend(v)
+                else:
+                    errors = e.message_dict
+            else:
+                errors[NON_FIELD_ERRORS] = e.messages
 
         if errors:
             raise ValidationError(errors)

Modified: django/branches/soc2009/model-validation/django/forms/models.py
===================================================================
--- django/branches/soc2009/model-validation/django/forms/models.py     
2009-06-01 15:39:21 UTC (rev 10870)
+++ django/branches/soc2009/model-validation/django/forms/models.py     
2009-06-01 15:39:46 UTC (rev 10871)
@@ -8,7 +8,7 @@
 from django.utils.text import get_text_list, capfirst
 from django.utils.translation import ugettext_lazy as _, ugettext
 
-from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
 from util import ErrorList
 from forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS
 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
@@ -27,20 +27,10 @@
     'ModelMultipleChoiceField',
 )
 
-
-def save_instance(form, instance, fields=None, fail_message='saved',
-                  commit=True, exclude=None):
-    """
-    Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
-
-    If commit=True, then the changes to ``instance`` will be saved to the
-    database. Returns ``instance``.
-    """
+def make_instance(form, instance, fields=None, exclude=None):
     from django.db import models
     opts = instance._meta
-    if form.errors:
-        raise ValueError("The %s could not be %s because the data didn't"
-                         " validate." % (opts.object_name, fail_message))
+
     cleaned_data = form.cleaned_data
     file_field_list = []
     for f in opts.fields:
@@ -65,9 +55,16 @@
     for f in file_field_list:
         f.save_form_data(instance, cleaned_data[f.name])
 
+    return instance
+
+def save_made_instance(form, instance, fields=None, commit=True, 
fail_message='saved'):
+    opts = instance._meta
+    if form.errors:
+        raise ValueError("The %s could not be %s because the data didn't"
+                         " validate." % (opts.object_name, fail_message))
+
     # Wrap up the saving of m2m data as a function.
     def save_m2m():
-        opts = instance._meta
         cleaned_data = form.cleaned_data
         for f in opts.many_to_many:
             if fields and f.name not in fields:
@@ -84,6 +81,18 @@
         form.save_m2m = save_m2m
     return instance
 
+
+def save_instance(form, instance, fields=None, fail_message='saved',
+                  commit=True, exclude=None):
+    """
+    Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
+
+    If commit=True, then the changes to ``instance`` will be saved to the
+    database. Returns ``instance``.
+    """
+    instance = make_instance(form, instance, fields, exclude)
+    return save_made_instance(form, instance, fields, commit, fail_message)
+
 def make_model_save(model, fields, fail_message):
     """Returns the save() method for a Form."""
     def save(self, commit=True):
@@ -218,7 +227,9 @@
             # if we didn't get an instance, instantiate a new one
             self.instance = opts.model()
             object_data = {}
+            self.__adding = True
         else:
+            self.__adding = False
             self.instance = instance
             object_data = model_to_dict(instance, opts.fields, opts.exclude)
         # if initial was provided, it should override the values from instance
@@ -228,6 +239,8 @@
                                             error_class, label_suffix, 
empty_permitted)
 
     def clean(self):
+        opts = self._meta
+        self.instance = make_instance(self, self.instance, opts.fields, 
opts.exclude)
         self.validate_unique()
         return self.cleaned_data
 
@@ -317,7 +330,7 @@
 
             # Exclude the current object from the query if we are editing an
             # instance (as opposed to creating a new one)
-            if self.instance.pk is not None:
+            if not self.__adding and self.instance.pk is not None:
                 qs = qs.exclude(pk=self.instance.pk)
 
             # This cute trick with extra/values is the most efficient way to
@@ -404,9 +417,9 @@
             fail_message = 'created'
         else:
             fail_message = 'changed'
-        return save_instance(self, self.instance, self._meta.fields,
-                             fail_message, commit, exclude=self._meta.exclude)
 
+        return save_made_instance(self, self.instance, self._meta.fields, 
commit, fail_message)
+
     save.alters_data = True
 
 class ModelForm(BaseModelForm):
@@ -731,6 +744,9 @@
 
             # Remove the foreign key from the form's data
             form.data[form.add_prefix(self.fk.name)] = None
+
+        # set the FK value here so that the form can do it's validation
+        setattr(form.instance, self.fk.get_attname(), self.instance.pk)
         return form
 
     #...@classmethod

Modified: 
django/branches/soc2009/model-validation/tests/regressiontests/views/views.py
===================================================================
--- 
django/branches/soc2009/model-validation/tests/regressiontests/views/views.py   
    2009-06-01 15:39:21 UTC (rev 10870)
+++ 
django/branches/soc2009/model-validation/tests/regressiontests/views/views.py   
    2009-06-01 15:39:46 UTC (rev 10871)
@@ -24,7 +24,7 @@
             model = Article
 
         def save(self, *args, **kwargs):
-            self.cleaned_data['slug'] = 'some-other-slug'
+            self.instance.slug = 'some-other-slug'
             return super(SlugChangingArticleForm, self).save(*args, **kwargs)
 
     return create_object(request,


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to