mirror of
https://github.com/django/django.git
synced 2025-07-07 19:29:12 +00:00
[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 git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@10871 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2f0e6d5595
commit
4d73902653
@ -296,7 +296,11 @@ class BaseGenericInlineFormSet(BaseModelFormSet):
|
|||||||
|
|
||||||
def __init__(self, data=None, files=None, instance=None, save_as_new=None, prefix=None):
|
def __init__(self, data=None, files=None, instance=None, save_as_new=None, prefix=None):
|
||||||
opts = self.model._meta
|
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((
|
self.rel_name = '-'.join((
|
||||||
opts.app_label, opts.object_name.lower(),
|
opts.app_label, opts.object_name.lower(),
|
||||||
self.ct_field.name, self.ct_fk_field.name,
|
self.ct_field.name, self.ct_fk_field.name,
|
||||||
@ -324,15 +328,20 @@ class BaseGenericInlineFormSet(BaseModelFormSet):
|
|||||||
self.ct_fk_field.name: self.instance.pk,
|
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.
|
# Avoid a circular import.
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
kwargs = {
|
form = super(BaseGenericInlineFormSet, self)._construct_form(i, **kwargs)
|
||||||
self.ct_field.get_attname(): ContentType.objects.get_for_model(self.instance).pk,
|
if self.save_as_new:
|
||||||
self.ct_fk_field.get_attname(): self.instance.pk,
|
# Remove the key from the form's data, we are only
|
||||||
}
|
# creating new instances
|
||||||
new_obj = self.model(**kwargs)
|
form.data[form.add_prefix(self.ct_fk_field.name)] = None
|
||||||
return save_instance(form, new_obj, commit=commit)
|
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,
|
def generic_inlineformset_factory(model, form=ModelForm,
|
||||||
formset=BaseGenericInlineFormSet,
|
formset=BaseGenericInlineFormSet,
|
||||||
|
@ -622,7 +622,14 @@ class Model(object):
|
|||||||
# TODO: run this only if not errors??
|
# TODO: run this only if not errors??
|
||||||
self.validate()
|
self.validate()
|
||||||
except ValidationError, e:
|
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:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
@ -8,7 +8,7 @@ from django.utils.datastructures import SortedDict
|
|||||||
from django.utils.text import get_text_list, capfirst
|
from django.utils.text import get_text_list, capfirst
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
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 util import ErrorList
|
||||||
from forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS
|
from forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS
|
||||||
from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
|
from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
|
||||||
@ -27,20 +27,10 @@ __all__ = (
|
|||||||
'ModelMultipleChoiceField',
|
'ModelMultipleChoiceField',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def make_instance(form, instance, fields=None, exclude=None):
|
||||||
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``.
|
|
||||||
"""
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
opts = instance._meta
|
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
|
cleaned_data = form.cleaned_data
|
||||||
file_field_list = []
|
file_field_list = []
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
@ -65,9 +55,16 @@ def save_instance(form, instance, fields=None, fail_message='saved',
|
|||||||
for f in file_field_list:
|
for f in file_field_list:
|
||||||
f.save_form_data(instance, cleaned_data[f.name])
|
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.
|
# Wrap up the saving of m2m data as a function.
|
||||||
def save_m2m():
|
def save_m2m():
|
||||||
opts = instance._meta
|
|
||||||
cleaned_data = form.cleaned_data
|
cleaned_data = form.cleaned_data
|
||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if fields and f.name not in fields:
|
if fields and f.name not in fields:
|
||||||
@ -84,6 +81,18 @@ def save_instance(form, instance, fields=None, fail_message='saved',
|
|||||||
form.save_m2m = save_m2m
|
form.save_m2m = save_m2m
|
||||||
return instance
|
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):
|
def make_model_save(model, fields, fail_message):
|
||||||
"""Returns the save() method for a Form."""
|
"""Returns the save() method for a Form."""
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
@ -218,7 +227,9 @@ class BaseModelForm(BaseForm):
|
|||||||
# if we didn't get an instance, instantiate a new one
|
# if we didn't get an instance, instantiate a new one
|
||||||
self.instance = opts.model()
|
self.instance = opts.model()
|
||||||
object_data = {}
|
object_data = {}
|
||||||
|
self.__adding = True
|
||||||
else:
|
else:
|
||||||
|
self.__adding = False
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
object_data = model_to_dict(instance, opts.fields, opts.exclude)
|
object_data = model_to_dict(instance, opts.fields, opts.exclude)
|
||||||
# if initial was provided, it should override the values from instance
|
# if initial was provided, it should override the values from instance
|
||||||
@ -228,6 +239,8 @@ class BaseModelForm(BaseForm):
|
|||||||
error_class, label_suffix, empty_permitted)
|
error_class, label_suffix, empty_permitted)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
opts = self._meta
|
||||||
|
self.instance = make_instance(self, self.instance, opts.fields, opts.exclude)
|
||||||
self.validate_unique()
|
self.validate_unique()
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
@ -317,7 +330,7 @@ class BaseModelForm(BaseForm):
|
|||||||
|
|
||||||
# Exclude the current object from the query if we are editing an
|
# Exclude the current object from the query if we are editing an
|
||||||
# instance (as opposed to creating a new one)
|
# 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)
|
qs = qs.exclude(pk=self.instance.pk)
|
||||||
|
|
||||||
# This cute trick with extra/values is the most efficient way to
|
# This cute trick with extra/values is the most efficient way to
|
||||||
@ -404,8 +417,8 @@ class BaseModelForm(BaseForm):
|
|||||||
fail_message = 'created'
|
fail_message = 'created'
|
||||||
else:
|
else:
|
||||||
fail_message = 'changed'
|
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
|
save.alters_data = True
|
||||||
|
|
||||||
@ -731,6 +744,9 @@ class BaseInlineFormSet(BaseModelFormSet):
|
|||||||
|
|
||||||
# Remove the foreign key from the form's data
|
# Remove the foreign key from the form's data
|
||||||
form.data[form.add_prefix(self.fk.name)] = None
|
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
|
return form
|
||||||
|
|
||||||
#@classmethod
|
#@classmethod
|
||||||
|
@ -24,7 +24,7 @@ def custom_create(request):
|
|||||||
model = Article
|
model = Article
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
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 super(SlugChangingArticleForm, self).save(*args, **kwargs)
|
||||||
|
|
||||||
return create_object(request,
|
return create_object(request,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user