If you add a unique constraint to a non-essential CharField, a DuplicateError will occur. I was a little addicted to creating a Field that saves Null instead of an empty string when there is no input, so I wrote a memo.
In django, if CharField or TextField is saved as empty, it will be saved as an empty string instead of Null. If IntegerField etc. is empty, Null is saved properly, so if you check the source of django what you are doing I found the following method in the IntegerField class.
def get_prep_value(self, value):
value = super(IntegerField, self).get_prep_value(value)
if value is None:
return None
return int(value)
This is like a method that converts the value before saving the DB, so if you return None when there is an empty string, Null will be saved.
class NullCharField(models.CharField):
def get_preg_value(self, value):
value = Super(NullCharField, self).get_preg_value(value)
return value if value else None
For the time being, this will save Null if there is no input in Form etc.
If you open the admin screen in this state, TypeError will occur.
Exception Value: coercing to Unicode: need string or buffer, NoneType found
Since it is said that "pass string or buffer", if it is None, it seems that processing to convert it to an empty string is necessary.
I thought I should rewrite the to_string method, but it doesn't work. It seems that it is necessary to pass django.db.models.SubfieldBase to the metaclass after various investigations. The value obtained from DB is entered by setattr (see Model class init method of django.db.models.base). If you pass SubfieldBase to the meta class, the to_python method will be called internally at setattr.
That's why I specified meta and added the processing at the time of acquisition.
from django.db import models
from django.utils import six
class NullCharField(six.with_metaclass(models.SubfieldBase, models.CharField)):
def get_preg_value(self, value):
value = super(NullCharField, self).get_preg_value(value)
return value if value else None
def to_python(self, value):
value = super(NullCharField, self).to_python(value)
return value if value is not None else u''
I thought it might be something, and when I looked it up, it was normal in oscar. https://github.com/django-oscar/django-oscar/blob/master/src/oscar/models/fields/init.py
class NullCharField(six.with_metaclass(SubfieldBase, CharField)):
"""
CharField that stores '' as None and returns None as ''
Useful when using unique=True and forms. Implies null==blank==True.
When a ModelForm with a CharField with null=True gets saved, the field will
be set to '': https://code.djangoproject.com/ticket/9590
This breaks usage with unique=True, as '' is considered equal to another
field set to ''.
"""
description = "CharField that stores '' as None and returns None as ''"
def __init__(self, *args, **kwargs):
if not kwargs.get('null', True) or not kwargs.get('blank', True):
raise ImproperlyConfigured(
"NullCharField implies null==blank==True")
kwargs['null'] = kwargs['blank'] = True
super(NullCharField, self).__init__(*args, **kwargs)
def to_python(self, value):
val = super(NullCharField, self).to_python(value)
return val if val is not None else u''
def get_prep_value(self, value):
prepped = super(NullCharField, self).get_prep_value(value)
return prepped if prepped != u"" else None
def deconstruct(self):
"""
deconstruct() is needed by Django's migration framework
"""
name, path, args, kwargs = super(NullCharField, self).deconstruct()
del kwargs['null']
del kwargs['blank']
return name, path, args, kwargs
It was almost there. I checked the null and blank flags with init. Also, it seems that deconstruct overwrite is necessary for migration.
Recommended Posts