from django.core import exceptions
from django.db import models
class NullCharField(models.CharField):
description = "CharField that transparently stores NULL for empty strings."
__metaclass__ = models.SubfieldBase # this ensures to_python will be called
def to_python(self, value):
# Is this the value right out of the db, or an instance? If an instance,
# just return the instance.
if isinstance(value, models.CharField):
return value
return value or '' # Transform NULLs to empty strings.
def get_prep_value(self, value):
return value or None # Transform empty strings to NULLs.
class NullEmailField(models.EmailField):
description = "EmailField that transparently stores NULL for empty strings."
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if isinstance(value, models.EmailField):
return value
return value or ''
def get_prep_value(self, value):
return value or None
class NullTrueField(models.NullBooleanField):
"""Allow only True or NULL (silently coerce False -> NULL).
This field type is useful when you have a field that you only want to allow 1
of. For example, a "primary" email address.
class Email(models.Model):
user = models.ForeignKey('auth.User')
primary = NullTrueField()
class Meta:
unique_together = ('user', 'primary')
By adding the ``unique_together`` constraint, we can enforce a single primary
email address per user (since NULL != NULL).
"""
description = "Boolean, Either True or None (no False allowed)."
__metaclass__ = models.SubfieldBase
def to_python(self, value):
"""Handle True/None normally, silently coerce False to None"""
if value is None:
return None
if value is True:
return True
if value in ('None',):
return None
if value in ('t', 'True', '1'):
return True
# Coerce False -> None
if value is False:
return None
if value in ('f', 'False', '0'):
return None
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
params={'value': value},
)
def get_prep_value(self, value):
value = super(NullTrueField, self).get_prep_value(value)
if bool(value):
return True
return None