It's important to solve this at the model level, not at the form level, since data can enter through APIs, through import scripts, from the shell, etc. The downside of setting null=True
on a CharField is that the column could end up with both empty strings and NULLs, which is slightly ambiguous but not generally a problem in my experience. If you're willing to live with that ambiguity, here's how to do it in a few steps:
1) Set null=True, blank=True
on the field and migrate in the change.
2) Massage your data so that all existing empty strings are changed to NULLs:
items = Foo.objects.all()
for item in items:
if not item.somefield:
item.somefield = None
item.save()
3) Add a custom save()
method to your model:
def save(self, *args, **kwargs):
# Empty strings are not unique, but we can save multiple NULLs
if not self.somefield:
self.somefield = None
super().save(*args, **kwargs) # Python3-style super()
4) Set unique=True
on the field and migrate that in as well.
Now you'll be able to store somefield
as empty or as a unique value whether you're using the admin or any other data entry method.
If you prefer not to have several migrations, here's an example of how to do it in a single migration:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def set_nulls(apps, schema_editor):
Event = apps.get_model("events", "Event")
events = Event.objects.all()
for e in events:
if not e.wdid:
e.wdid = None
e.save()
class Migration(migrations.Migration):
dependencies = [
('events', '0008_something'),
]
operations = [
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.RunPython(set_nulls),
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True, unique=True),
),
]