I saw some ppl had this problem before me, but on older versions of Django, and I\'m running on 1.2.1.
I have a model that looks like:
class Category
I had that problem too and solved it by creating a supermodel with clean
method (like Alasdair suggested) and use it as base class for all my models:
class Base_model(models.Model):
class Meta:
abstract=True
def clean(self):
"""
Check for instances with null values in unique_together fields.
"""
from django.core.exceptions import ValidationError
super(Base_model, self).clean()
for field_tuple in self._meta.unique_together[:]:
unique_filter = {}
unique_fields = []
null_found = False
for field_name in field_tuple:
field_value = getattr(self, field_name)
if getattr(self, field_name) is None:
unique_filter['%s__isnull'%field_name] = True
null_found = True
else:
unique_filter['%s'%field_name] = field_value
unique_fields.append(field_name)
if null_found:
unique_queryset = self.__class__.objects.filter(**unique_filter)
if self.pk:
unique_queryset = unique_queryset.exclude(pk=self.pk)
if unique_queryset.exists():
msg = self.unique_error_message(self.__class__, tuple(unique_fields))
raise ValidationError(msg)
The unique together constraint is enforced at the database level, and it appears that your database engine does not apply the constraint for null values.
In Django 1.2, you can define a clean method for your model to provide custom validation. In your case, you need something that checks for other categories with the same name whenever the parent is None.
class Category(models.Model):
...
def clean(self):
"""
Checks that we do not create multiple categories with
no parent and the same name.
"""
from django.core.exceptions import ValidationError
if self.parent is None and Category.objects.filter(name=self.name, parent=None).exists():
raise ValidationError("Another Category with name=%s and no parent already exists" % self.name)
If you are editing categories through the Django admin, the clean method will be called automatically. In your own views, you must call category.fullclean()
.
Unfortunately, for those of us using PostgreSQL as our backend database engine, there will never have a fix for this issue:
"Currently, only B-tree indexes can be declared unique.
When an index is declared unique, multiple table rows with equal indexed values are not allowed. Null values are not considered equal. A multicolumn unique index will only reject cases where all indexed columns are equal in multiple rows.
PostgreSQL automatically creates a unique index when a unique constraint or primary key is defined for a table. The index covers the columns that make up the primary key or unique constraint (a multicolumn index, if appropriate), and is the mechanism that enforces the constraint."
Source: https://www.postgresql.org/docs/9.0/indexes-unique.html