On Jan 18, 5:17 am, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:
> (b) Please do write it out and post it here so that we can have the
> discussion on the mailing list.

Let’s step back, distance ourselves from the current implementation
and look at how forms, models and modelforms should ideally
interact validation-wise at a somewhat abstract level.

The main focus is on fields.

Form fields
===========

In validation context, a form field has the following
responsibilities:

 * get input value (a string) from user,

 * validate that the value is in expected format
   (*format validation*, preconditions),

 * convert the value to a Python type,

 * validate that the resulting value satisfies any conditions set
   for the value (*value validation*, postconditions).

Model fields
============

Ditto for model fields, omitting the first step: format validation,
coercion to Python and value validation is generally required.

If, however, we want to more radically depart from current
behaviour, then it is possible and indeed reasonable to assume that
the values assigned to model fields are already the expected Python
types, as it is the application developer’s responsibility to assure
that. This would get rid of the format validation and coercion step
(these are already handled by the form layer). to_python() is
currently used in model fields for controlling the conversion.

Interaction between form and model fields
=========================================

Forms and models should be orthogonal and oblivious of each other.
Modelforms serves as the glue and mapping between the two, being
aware of the internals of both.

A modelform should have the following responsibilites in validation
context:

 * clean the form (performs format validation, coercion to a Python
   type and value validation),

 * assign the form field values to the associated model fields,

 * inform the model fields that basic validation has already been done
to
   avoid duplicated validation and call any additional validation
   methods.

Thus, the modelform should be able to invoke only the additional
and custom model field validators, *skipping the default coercers
and validators* that can be assumed to be the same in similar form
and model field classes (e.g. an IntegerField in forms and
IntegerField in models).

Let's look at an contrived example that illustrates the point.

Let IntegerForeignKeyField be an imaginary foreign key field in a
model that accepts only integer foreign keys. The corresponding
form field would be a PositiveIntegerField. Suppose that the
developer has provided a custom validator for the
IntegerForeignKeyField that allows foreign keys only to objects
where the is_active boolean field is True.

When a modelform uses them both, it is the form field's
responsibility to assure that the value is converted to a Python
int and that the int is positive. IntegerForeignKeyField should
not re-run neither the coercion nor basic validation routines.
However, IntegerForeignKeyField must

1) validate that the integer points to an existing row in the
   related table (i.e. run a foreignkey-specific validator),

2) validate that the row has is_active == True (i.e. run a custom
   validator).

In this case, coercion and basic validation is cheap, but that's
not always the case.

General validation principles
=============================

The extended conclusion of the sections above is as follows.

Double validation should never happen. There has to be a way to
convey that a value is already validated and no further validation
is necessary. No code duplication in type coercion or validation
should occur in models and forms. The following distinct
converters and validators participate in the coercion/validation
process:

 * format validators that validate that a given string can be
   converted to a Python type,

 * converters that coerce a given string to a Python type,

 * value validators that assure that the value returned by a
   converter satisfies any conditions set for the value.

It should be easy to add custom validators and intercept or
override the process at any step.

And now something completely different
======================================

"Every problem in computer science can be solved by
another level of indirection."
 --- source unknown

Both form and model fields need similar functionality that is not
well served by just duplicating validation and coercion functions
in them.

It would make sense to factor out the common functionality and
decouple it from both. A mixin class would serve that purpose well.

The following methods would live in the mixin:
 * clean() that calls to_python() and then validate(),
 * to_python() that takes care of format validation and coercion,
 * validate() that takes care of value validation and running
   custom validators.

It would have fields for storing the default and custom validators
and perhaps a state field for controlling which parts of the
validation process have already been run.

I've hacked up an incomplete rough draft at
http://dpaste.com/110671/ to illustrate this. Unfortunately it does
not illustrate all the points, but the general idea should
hopefully come through.

Action plan for 1.1
===================

The mixin approach is not in scope for 1.1. We go with what we
already have, factoring the bits in current model fields' to_python
to a separate django.utils.typeconverters library
(see 
http://github.com/mrts/honza-django/commit/a8239b063591acc367add0a01785181a91a37971
for the first steps in that direction) and using both
django.core.validators and django.utils.typeconverters throughout
model and form fields.

I'm not sure about Honza's plans in regard avoiding duplicate
validation, hopefully he comments on that himself.

Also, let me remind that model and form objects have not been
discussed in this post (and it's already more than 150 lines long),
only fields.
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to