From 85ef615c42969d92789cd6fc4dada23379618a68 Mon Sep 17 00:00:00 2001 From: Robert Wittams Date: Thu, 29 Dec 2005 21:42:39 +0000 Subject: [PATCH] magic-removal: Some validation fixes. git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1797 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/views/stages/change.py | 48 ++++++------ django/core/formfields.py | 87 +++++++++++++++------ django/db/models/base.py | 4 +- django/db/models/fields/__init__.py | 5 +- django/db/models/manipulators.py | 53 +++++++++++-- 5 files changed, 137 insertions(+), 60 deletions(-) diff --git a/django/contrib/admin/views/stages/change.py b/django/contrib/admin/views/stages/change.py index c0cb8b8636..a632193dec 100644 --- a/django/contrib/admin/views/stages/change.py +++ b/django/contrib/admin/views/stages/change.py @@ -49,41 +49,43 @@ def change_stage(request, path, object_id): #save a copy of the data to use for errors later. data = new_data.copy() - manipulator.do_html2python(new_data) #update the manipulator with the effects of previous commands. manipulator.update(new_data) #get the errors on the updated shape of the manipulator #HACK - validators should not work on POSTED data directly... - errors = manipulator.get_validation_errors(data) + if request.POST.has_key("_preview"): - pass + errors = manipulator.get_validation_errors(data) elif request.POST.has_key("command"): command_name = request.POST.get("command") manipulator.do_command(command_name) - new_data = manipulator.flatten_data() - elif errors: + errors = manipulator.get_validation_errors(data) new_data = manipulator.flatten_data() else: - new_object = manipulator.save_from_update() - log_change_message(request.user, opts, manipulator, new_object) - msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object} - pk_value = getattr(new_object, opts.pk.attname) - if request.POST.has_key("_continue"): - request.user.add_message(msg + ' ' + _("You may edit it again below.")) - if request.REQUEST.has_key('_popup'): - return HttpResponseRedirect(request.path + "?_popup=1") - else: - return HttpResponseRedirect(request.path) - elif request.POST.has_key("_saveasnew"): - request.user.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object}) - return HttpResponseRedirect("../../%s/" % pk_value) - elif request.POST.has_key("_addanother"): - request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) - return HttpResponseRedirect("../../add/") + errors = manipulator.get_validation_errors(data) + if errors: + new_data = manipulator.flatten_data() else: - request.user.add_message(msg) - return HttpResponseRedirect("../../") + new_object = manipulator.save_from_update() + log_change_message(request.user, opts, manipulator, new_object) + msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object} + pk_value = getattr(new_object, opts.pk.attname) + if request.POST.has_key("_continue"): + request.user.add_message(msg + ' ' + _("You may edit it again below.")) + if request.REQUEST.has_key('_popup'): + return HttpResponseRedirect(request.path + "?_popup=1") + else: + return HttpResponseRedirect(request.path) + elif request.POST.has_key("_saveasnew"): + request.user.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object}) + return HttpResponseRedirect("../../%s/" % pk_value) + elif request.POST.has_key("_addanother"): + request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) + return HttpResponseRedirect("../../add/") + else: + request.user.add_message(msg) + return HttpResponseRedirect("../../") else: # Populate new_data with a "flattened" version of the current data. new_data = manipulator.flatten_data() diff --git a/django/core/formfields.py b/django/core/formfields.py index f751ed287e..08c2af5102 100644 --- a/django/core/formfields.py +++ b/django/core/formfields.py @@ -55,26 +55,35 @@ class Manipulator(object): "Returns dictionary mapping field_names to error-message lists" errors = {} for field in self.fields: - if field.is_required and not new_data.get(field.field_name, False): - errors.setdefault(field.field_name, []).append(gettext_lazy('This field is required.')) - continue - try: - validator_list = field.validator_list - if hasattr(self, 'validate_%s' % field.field_name): - validator_list.append(getattr(self, 'validate_%s' % field.field_name)) - for validator in validator_list: - if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'): - try: - if hasattr(field, 'requires_data_list'): - validator(new_data.getlist(field.field_name), new_data) - else: - validator(new_data.get(field.field_name, ''), new_data) - except validators.ValidationError, e: - errors.setdefault(field.field_name, []).extend(e.messages) - # If a CriticalValidationError is raised, ignore any other ValidationErrors - # for this particular field - except validators.CriticalValidationError, e: - errors.setdefault(field.field_name, []).extend(e.messages) + errors.update(field.get_validation_errors(new_data)) + val_name = 'validate_%s' % field.field_name + if hasattr(self, val_name): + val = getattr(self, val_name) + try: + field.run_validator(new_data, val) + except (validators.ValidationError, validators.CriticalValidationError), e: + errors.setdefault(field.field_name, []).extend(e.messages) + +# if field.is_required and not new_data.get(field.field_name, False): +# errors.setdefault(field.field_name, []).append(gettext_lazy('This field is required.')) +# continue +# try: +# validator_list = field.validator_list +# if hasattr(self, 'validate_%s' % field.field_name): +# validator_list.append(getattr(self, 'validate_%s' % field.field_name)) +# for validator in validator_list: +# if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'): +# try: +# if hasattr(field, 'requires_data_list'): +# validator(new_data.getlist(field.field_name), new_data) +# else: +# validator(new_data.get(field.field_name, ''), new_data) +# except validators.ValidationError, e: +# errors.setdefault(field.field_name, []).extend(e.messages) +# # If a CriticalValidationError is raised, ignore any other ValidationErrors +# # for this particular field +# except validators.CriticalValidationError, e: +# errors.setdefault(field.field_name, []).extend(e.messages) return errors def save(self, new_data): @@ -89,6 +98,7 @@ class Manipulator(object): must happen after validation because html2python functions aren't expected to deal with invalid input. """ + print "converting for ", self, self.fields for field in self.fields: field.convert_post_data(new_data) @@ -314,12 +324,37 @@ class FormField: except ValueError: converted_data = d new_data.setlist(name, converted_data) -# else: -# try: -# # individual fields deal with None values themselves -# new_data.setlist(name, [self.__class__.html2python(None)]) -# except EmptyValue: -# new_data.setlist(name, []) + else: + try: + #individual fields deal with None values themselves + new_data.setlist(name, [self.__class__.html2python(None)]) + except EmptyValue: + new_data.setlist(name, []) + + + def run_validator(self, new_data, validator): + if self.is_required or new_data.get(self.field_name, False) or hasattr(validator, 'always_test'): + if hasattr(self, 'requires_data_list'): + validator(new_data.getlist(self.field_name), new_data) + else: + validator(new_data.get(self.field_name, ''), new_data) + + def get_validation_errors(self, new_data): + errors = {} + if self.is_required and not new_data.get(self.field_name, False): + errors.setdefault(self.field_name, []).append(gettext_lazy('This field is required.')) + return errors + try: + for validator in self.validator_list: + try: + self.run_validator(new_data, validator) + except validators.ValidationError, e: + errors.setdefault(self.field_name, []).extend(e.messages) + # If a CriticalValidationError is raised, ignore any other ValidationErrors + # for this particular field + except validators.CriticalValidationError, e: + errors.setdefault(self.field_name, []).extend(e.messages) + return errors def get_id(self): "Returns the HTML 'id' attribute for this form field." diff --git a/django/db/models/base.py b/django/db/models/base.py index 7f2497a29c..88b7205015 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1,6 +1,6 @@ import django.db.models.manipulators import django.db.models.manager -from django.db.models.fields import AutoField, ImageField +from django.db.models.fields import AutoField, ImageField, Admin from django.db.models.fields.related import OneToOne, ManyToOne from django.db.models.related import RelatedObject from django.db.models.query import orderlist2sql @@ -20,7 +20,7 @@ import os if not hasattr(__builtins__, 'set'): from sets import Set as set -attribute_transforms = {} +attribute_transforms = { 'Admin': lambda cls: Admin(**cls.__dict__) } class ModelBase(type): "Metaclass for all models" diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 6590e86a01..06cac41fbd 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -726,7 +726,7 @@ class OrderingField(IntegerField): return "IntegerField" def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): - return [HiddenField(name_prefix + self.name) ] + return [formfields.HiddenField(name_prefix + self.name) ] def contribute_to_class(self, cls, name): super(OrderingField, self ).contribute_to_class(cls, name) @@ -819,3 +819,6 @@ class Admin: line_specs = fs_options['fields'] new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs)) return new_fieldset_list + + def contribute_to_class(self, cls, name): + cls._meta.admin = self \ No newline at end of file diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py index 50e4c4b7cc..d27e770f32 100644 --- a/django/db/models/manipulators.py +++ b/django/db/models/manipulators.py @@ -99,20 +99,38 @@ class AutomaticManipulator(Manipulator, Naming): if manipulators != None: self.children[f] = manipulators self.needs_deletion = False + self.ignore_errors = False def get_fields(self): - if self.needs_deletion: + if self.needs_deletion : return [] else: - l = list(self.fields_) - for child_manips in self.children.values(): - for manip in child_manips: - if manip: - l.extend(manip.fields) - return l + return self.fields_ + #l = list(self.fields_) + #for child_manips in self.children.values(): + # for manip in child_manips: + # if manip: + # l.extend(manip.fields) + #return l fields = property(get_fields) + def get_validation_errors(self, new_data): + "Returns dictionary mapping field_names to error-message lists" + if self.needs_deletion or self.ignore_errors: + return {} + + errors = super(AutomaticManipulator, self).get_validation_errors(new_data) + + for manips in self.children.values(): + errors.update(manips.get_validation_errors(new_data)) + return errors + + def do_html2python(self, new_data): + super(AutomaticManipulator, self).do_html2python(new_data) + for child in self.children.values(): + child.do_html2python(new_data) + def get_original_value(self, field): raise NotImplemented @@ -358,6 +376,8 @@ class ManipulatorCollection(list, Naming): items.sort(cmp = lambda x, y: cmp(x[0], y[0])) for index, obj_data in items: child_manip = self.add_child(index) + #HACK: this data will not have been converted to python form yet. + #child_manip.do_html2python(obj_data) child_manip._fill_data(obj_data) def _do_command_expanded(self, command_parts): @@ -384,7 +404,9 @@ class ManipulatorCollection(list, Naming): # add. # TODO: page.forward, page.back, page.n, swap.n.m if command_name == "add": - self.add_child() + child_manip = self.add_child() + # Don't show validation stuff for things just added. + child_manip.ignore_errors = True elif command_name == "swap": order_field = self.model._meta.order_with_respect_to if not order_field: @@ -417,6 +439,7 @@ class ManipulatorCollection(list, Naming): prefix = '%s%s.' % (self.name_prefix, index ) child_manip = man_class(self.follow, self.name_parts + ( str(index), ) ) + self[index] = child_manip return child_manip @@ -427,6 +450,20 @@ class ManipulatorCollection(list, Naming): manip_data = manip.flatten_data() new_data.update(manip_data) return new_data + + def get_validation_errors(self, new_data): + "Returns dictionary mapping field_names to error-message lists" + errors = {} + for manip in self: + if manip: + errors.update(manip.get_validation_errors(new_data)) + return errors + + def do_html2python(self, new_data): + print "coll: ", self + for manip in self: + if manip: + manip.do_html2python(new_data) def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): from django.db.models.fields.related import ManyToOne