title: Django model fields validation
body: <p>Where should the validation of <em>model fields</em> go in django?</p>
  
  <p>I could name at least two possible choices: in the overloaded .save() method of the model or in the .to_python() method of the models.Field subclass (obviously for that to work you must write custom fields).</p>
  
  <p>Possible use cases:</p>
  
  <ul>
  <li>when it is absolutely neccessary to ensure, that an empty string doesn't get written into the database (blank=False keyword argument doesn't work here, it is for form validation only)</li>
  <li>when it is neccessary to ensure, that "choices" keyword argument gets respected on a db-level and not only in admin interface (kind of emulating a enum datatype)</li>
  </ul>
  
  <p>There is also a class-level attribute <code>empty_strings_allowed</code> in the models.Field base class definition and derived classes happily override it, however it doesn't seem to produce any effect on the database level, meaning I can still construct a model with empty-string fields and save it to the database. Which I want to avoid (yes, it is neccessary).</p>
  
  <p>Possible implementations are</p>
  
  <p>on the field level:</p>
  
  <pre><code>class CustomField(models.CharField):
      __metaclass__ = models.SubfieldBase
      def to_python(self, value):
          if not value:
              raise IntegrityError(_('Empty string not allowed'))
          return models.CharField.to_python(self, value)
  </code></pre>
  
  <p>on the model level:</p>
  
  <pre><code>class MyModel(models.Model)
      FIELD1_CHOICES = ['foo', 'bar', 'baz']
      field1 = models.CharField(max_length=255, 
                 choices=[(item,item) for item in FIELD1_CHOICES])
  
      def save(self, force_insert=False, force_update=False):
          if self.field1 not in MyModel.FIELD1_CHOICES:
              raise IntegrityError(_('Invalid value of field1'))
          # this can, of course, be made more generic
          models.Model.save(self, force_insert, force_update)
  </code></pre>
  
  <p>Perhaps, I am missing something and this can be done easier (and cleaner)?</p>