After several discussions with Honza, we are still on somewhat different positions what the validator function signature should be and how core validators should access the fields of a form or a model instance.
In core validators, no special case handling of forms and models is needed even in multi-value validators. All what is needed is an abstraction of "the set of other values". AlwaysMatchesOtherField, RequiredIfOtherFieldDoesNotEqual etc will work with that abstraction, primary keys and other complex model fields also work for same-type comparison. My initial idea was to reduce the model instance to a dict (as discussed previously), however, this is really too restrictive. Passing the form or model instance and treating it in dict-like manner provides much more flexibility. That can be achieved by the following: class Model(...): ... def get_value(self, field_name, *args): "Default is specified with the first positional argument" if args: return getattr(self, field_name, args[0]) return getattr(self, field_name) class BaseForm(...) ... def get_value(self, field_name, *args): "Default is specified with the first positional argument" if args: return self.cleaned_data.get(field_name, args[0]) return self.cleaned_data[field_name] (It's not possible to implement the __getitem__ protocol as it already works differently in forms.) In this case the form/model dichotomy is not required in validator signature, simple validators work as follows: def is_slug(value, instance=None): (access only value) and multi-value validators as follows: class AlwaysMatchesOtherField(object): ... def __call__(self, value, instance=None): if value != instance.get_value(self.other): raise ValidationError(...) There are currently no model-specific complex validators. If a need for them should arise, it's probably easiest to override validate() in the corresponding class, calling super().validate() and then perform any additional checks there. Custom validators can either use the get_value() abstraction to support both models and forms or be model/form-specific and use the instance in whatever way they like. Checking whether you deal with a model or form instance is generally not required -- if you accidentally pass a custom form validator to a model, it will visibly break and vice-versa. Honza doesn't like this, mainly because it's too easy to shoot yourself into foot with this (as outlined above, I disagree with that) and as of now we are using the following instead to handle model and form fields uniformly: def _get_value(name, all_values, instance, do_rise=True): if instance is not None and hasattr(instance, name): return getattr(instance, name) if name in all_values: return all_values[name] if do_raise: raise AttributeError('%s not found in form values or model instance' % name) return False class AlwaysMatchesOtherField(object): ... def __call__(self, value, all_values={}, instance=None): if value != _get_value(self.other, all_values, instance): raise ValidationError(...) Honza's comments: ----------------- The problem is that I do not want Form class to be passed to validators for several reasons: Form shouldn't be anything except something that obtains data from request and can validate them, passing it validators could promote adding some methods and properties to be used in validators, instead of in the form's clean() method. Form and model treat data differently, that's just the way it is and as it should be - pretending it's not true will just create opportunity for nasty surprises. Validators wouldn't work outside forms and models (it's easy to pass in a dictionary of all_values, but fabricating a class with the same method is work). I would still have to check whether I am dealing with model or form when I would like to access some model's methods and properties that cannot be retrieved this way. I have more reasons, but these are the major ones, if it's not enough, contact me and we can talk ;). --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-developers?hl=en -~----------~----~----~----~------~----~------~--~---