Django

Code

Changeset 2518

Show
Ignore:
Timestamp:
03/12/06 19:33:45 (3 years ago)
Author:
adrian
Message:

magic-removal: Added first bit of validation-aware models. Model objects now have a validate() method. See docstrings in db.models.base and db.models.fields.init for information. Also added unit tests for all the currently supported validation.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/magic-removal/django/db/models/base.py

    r2506 r2518  
    11import django.db.models.manipulators 
    22import django.db.models.manager 
     3from django.core import validators 
     4from django.core.exceptions import ObjectDoesNotExist 
    35from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist 
    46from django.db.models.fields.related import OneToOne, ManyToOne 
     
    1012from django.db.models.loading import register_models 
    1113from django.dispatch import dispatcher 
    12 from django.core.exceptions import ObjectDoesNotExist 
    1314from django.utils.datastructures import SortedDict 
    1415from django.utils.functional import curry 
     
    3940        # Build complete list of parents 
    4041        for base in bases: 
     42            # TODO: Checking for the presence of '_meta' is hackish. 
    4143            if '_meta' in dir(base): 
    4244                new_class._meta.parents.append(base) 
     
    197199    save.alters_data = True 
    198200 
     201    def validate(self): 
     202        """ 
     203        First coerces all fields on this instance to their proper Python types. 
     204        Then runs validation on every field. Returns a dictionary of 
     205        field_name -> error_list. 
     206        """ 
     207        error_dict = {} 
     208        invalid_python = {} 
     209        for f in self._meta.fields: 
     210            try: 
     211                setattr(self, f.attname, f.to_python(getattr(self, f.attname, f.get_default()))) 
     212            except validators.ValidationError, e: 
     213                error_dict[f.name] = e.messages 
     214                invalid_python[f.name] = 1 
     215        for f in self._meta.fields: 
     216            if f.name in invalid_python: 
     217                continue 
     218            errors = f.validate_full(getattr(self, f.attname, f.get_default()), self.__dict__) 
     219            if errors: 
     220                error_dict[f.name] = errors 
     221        return error_dict 
     222 
    199223    def _collect_sub_objects(self, seen_objs): 
    200224        """ 
  • django/branches/magic-removal/django/db/models/fields/__init__.py

    r2517 r2518  
    77from django.utils.functional import curry, lazy 
    88from django.utils.text import capfirst 
    9 from django.utils.translation import gettext_lazy, ngettext 
    10 import datetime, os 
     9from django.utils.translation import gettext, gettext_lazy, ngettext 
     10import datetime, os, time 
    1111 
    1212class NOT_PROVIDED: 
     
    3838    if getattr(self, 'original_object', None) and self.original_object._get_pk_val() == old_obj._get_pk_val(): 
    3939        return 
    40     raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name} 
     40    raise validators.ValidationError, gettext("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name} 
    4141 
    4242# A guide to Field parameters: 
     
    9797        return cmp(self.creation_counter, other.creation_counter) 
    9898 
     99    def to_python(self, value): 
     100        """ 
     101        Converts the input value into the expected Python data type, raising 
     102        validators.ValidationError if the data can't be converted. Returns the 
     103        converted value. Subclasses should override this. 
     104        """ 
     105        return value 
     106 
     107    def validate_full(self, field_data, all_data): 
     108        """ 
     109        Returns a list of errors for this field. This is the main interface, 
     110        as it encapsulates some basic validation logic used by all fields. 
     111        Subclasses should implement validate(), not validate_full(). 
     112        """ 
     113        if not self.blank and not field_data: 
     114            return [gettext_lazy('This field is required.')] 
     115        try: 
     116            self.validate(field_data, all_data) 
     117        except validators.ValidationError, e: 
     118            return e.messages 
     119        return [] 
     120 
     121    def validate(self, field_data, all_data): 
     122        """ 
     123        Raises validators.ValidationError if field_data has any errors. 
     124        Subclasses should override this to specify field-specific validation 
     125        logic. This method should assume field_data has already been converted 
     126        into the appropriate data type by Field.to_python(). 
     127        """ 
     128        pass 
     129 
    99130    def set_attributes_from_name(self, name): 
    100131        self.name = name 
     
    300331    def __init__(self, *args, **kwargs): 
    301332        assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__ 
     333        kwargs['blank'] = True 
    302334        Field.__init__(self, *args, **kwargs) 
     335 
     336    def to_python(self, value): 
     337        if value is None: 
     338            return value 
     339        try: 
     340            return int(value) 
     341        except (TypeError, ValueError): 
     342            raise validators.ValidationError, gettext("This value must be an integer.") 
    303343 
    304344    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 
     
    328368        Field.__init__(self, *args, **kwargs) 
    329369 
     370    def to_python(self, value): 
     371        if value in (True, False): return value 
     372        if value is 't': return True 
     373        if value is 'f': return False 
     374        raise validators.ValidationError, gettext("This value must be either True or False.") 
     375 
    330376    def get_manipulator_field_objs(self): 
    331377        return [forms.CheckboxField] 
     
    335381        return [forms.TextField] 
    336382 
     383    def to_python(self, value): 
     384        if isinstance(value, basestring): 
     385            return value 
     386        if value is None: 
     387            if self.null: 
     388                return value 
     389            else: 
     390                raise validators.ValidationError, gettext_lazy("This field cannot be null.") 
     391        return str(value) 
     392 
     393# TODO: Maybe move this into contrib, because it's specialized. 
    337394class CommaSeparatedIntegerField(CharField): 
    338395    def get_manipulator_field_objs(self): 
     
    349406        Field.__init__(self, verbose_name, name, **kwargs) 
    350407 
     408    def to_python(self, value): 
     409        if isinstance(value, datetime.datetime): 
     410            return value.date() 
     411        if isinstance(value, datetime.date): 
     412            return value 
     413        validators.isValidANSIDate(value, None) 
     414        return datetime.date(*time.strptime(value, '%Y-%m-%d')[:3]) 
     415 
    351416    def get_db_prep_lookup(self, lookup_type, value): 
    352417        if lookup_type == 'range': 
     
    392457 
    393458class DateTimeField(DateField): 
     459    def to_python(self, value): 
     460        if isinstance(value, datetime.datetime): 
     461            return value 
     462        if isinstance(value, datetime.date): 
     463            return datetime.datetime(value.year, value.month, value.day) 
     464        try: # Seconds are optional, so try converting seconds first. 
     465            return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6]) 
     466        except ValueError: 
     467            try: # Try without seconds. 
     468                return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5]) 
     469            except ValueError: # Try without hour/minutes/seconds. 
     470                try: 
     471                    return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3]) 
     472                except ValueError: 
     473                    raise validators.ValidationError, gettext('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 
     474 
    394475    def get_db_prep_save(self, value): 
    395476        # Casts dates into string format for entry into database. 
     
    433514                time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 
    434515 
    435 class EmailField(Field): 
     516class EmailField(CharField): 
    436517    def __init__(self, *args, **kwargs): 
    437518        kwargs['maxlength'] = 75 
    438         Field.__init__(self, *args, **kwargs) 
     519        CharField.__init__(self, *args, **kwargs) 
    439520 
    440521    def get_internal_type(self): 
     
    443524    def get_manipulator_field_objs(self): 
    444525        return [forms.EmailField] 
     526 
     527    def validate(self, field_data, all_data): 
     528        validators.isValidEmail(field_data, all_data) 
    445529 
    446530class FileField(Field): 
     
    584668        return [forms.IPAddressField] 
    585669 
     670    def validate(self, field_data, all_data): 
     671        validators.isValidIPAddress4(field_data, None) 
     672 
    586673class NullBooleanField(Field): 
    587674    def __init__(self, *args, **kwargs): 
     
    595682    def get_manipulator_field_objs(self): 
    596683        return [forms.PhoneNumberField] 
     684 
     685    def validate(self, field_data, all_data): 
     686        validators.isValidPhone(field_data, all_data) 
    597687 
    598688class PositiveIntegerField(IntegerField): 
     
    627717    empty_strings_allowed = False 
    628718    def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs): 
    629         self.auto_now, self.auto_now_add = auto_now, auto_now_add 
     719        self.auto_now, self.auto_now_add = auto_now, auto_now_add 
    630720        if auto_now or auto_now_add: 
    631721            kwargs['editable'] = False