Just got back from a week-long Hawaii vacation, so I'm getting back
into the Django groove. Earlier today I committed the first stab at
validation-aware models on the magic-removal branch:

http://code.djangoproject.com/changeset/2518

Here's how it works:

* Model instances have a validate() method. It returns a dictionary of
field name -> list of error messages. If there are no errors, it
returns an empty dictionary.

* Behind the scenes, validate() actually does a two-pass validation
over all the model instance's fields:

    * First, it runs the field's to_python() method, which is
field-type-specific and converts the data to the appropriate Python
type. For example, if a date field is set to '2005-01-03', to_python()
converts it to a datetime object.

    * Second, for all fields that didn't have validation errors in
to_python(), it executes the field's validate_full() method. That
method can safely assume the data is in the correct data type.

The separation of "type-specific" validation and "logical" validation
makes things a lot cleaner, both in concept and implementation. It's
sort of like the CriticalValidationError framework in
django/core/formfields.py in trunk.

* Model instances' save() method will call validate() behind the
scenes, and the object won't save if there are any validation errors.
I haven't coded this part yet, but it's a simple change.

* I've only implemented to_python() and validate_full() for a handful
of field types in django/db/models/fields/__init__.py. The rest still
need to be implemented.

* Every one of the to_python() and validate_full() functions that
*are* implemented (to my knowledge) have unit tests in
tests/modeltests/validation/models.py. As we implement to_python() and
validate_full() for other field types, they should get unit tests
immediately.

EXAMPLE CODE:

class Person(models.Model):
    name = models.CharField(maxlength=50)
    birthday = models.DateField()
    favorite_number = models.IntegerField()

>>> p = Person(name='Joe', birthday='never', favorite_number='foo')
>>> p.validate()
{'birthday': 'Enter a valid date in YYYY-MM-DD format.',
'favorite_number': 'This value must be an integer.'}
>>> p = Person(name='Joe', birthday='2005-1-3', favorite_number='34')
>>> p.validate()
{}
>>> p.birthday
datetime.date(2005, 1, 3)
>>> p.favorite_number # note this is an integer now, no longer a string
34


Thoughts, criticisms? The only part I'm sketchy on is the side effect
of converting the values to their Python data types. The
cleanliness/consistency is quite nice, but it's slightly not something
a developer might expect. However, we can solve the latter through
clear documentation.

Adrian

--
Adrian Holovaty
holovaty.com | djangoproject.com

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" 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-developers
-~----------~----~----~----~------~----~------~--~---

Reply via email to