26👍
In Postgresql NULL
isn’t equal to any other NULL
. Therefore the rows you create are not the same (from Postgres’ perspective).
Update
You have a few ways to deal with it:
- Forbid the
Null
value for foreign key and use some default value - Override the
save
method of your model to check that no such row exists - Change SQL standard 🙂
1👍
Add a clean
method to your model, so you can edit an existing row.
def clean(self):
queryset = MO.objects.exclude(id=self.id).filter(slug=self.slug)
if self.foreign_key is None:
if queryset.exists():
raise ValidationError("A row already exists with this slug and no key")
else:
if queryset.filter(foreign_key=self.foreign_key).exists():
raise ValidationError("This row already exists")
Beware, clean
(or full_clean
) isn’t called by the default save
method.
NB: if you put this code in the save
method, update forms (like in the admin) won’t work: you will have a traceback error due to the ValidationError
exception.
- Django template object type
- How do I handle file upload via PUT request in Django?
- Django queryset order_by dates near today
- Checking if a Django user has a password set
0👍
Just manually create secondary index on slug
field, but only for NULL values in foreign_key_id
:
CREATE INDEX table_name_unique_null_foreign_key
ON table_name (slug) WHERE foreign_key_id is NULL
Please note, that Django does not support this, so without custom form/model validation you will get pure IntegrityError / 500.
Possible duplicate of Create unique constraint with null columns
- How can I disable a model field in a django form
- Django is very slow on my machine
- Django; AWS Elastic Beanstalk ERROR: Your WSGIPath refers to a file that does not exist
- How to assign to a Django PointField model attribute?
0👍
As hobbyte mentioned, “In Postgresql NULL isn’t equal to any other NULL. Therefore the rows you create are not the same (from Postgres’ perspective).”
Another possible way to address this challenge is to add custom validation at the view level in the form_valid method.
In views.py:
def form_valid(self, form):
--OTHER VALIDATION AND FIELD VALUE ASSIGNMENT LOGIC--
if ModelForm.objects.filter(slug=slug,foreign_key=foreign_key:
form.add_error('field',
forms.ValidationError( _("Validation error message that shows up in your form. "),
code='duplicate_row', ))
return self.form_invalid(form)
This approach is helpful if you are using class based views, especially if you are automatically assigning values to fields that you want to hide from the user.
Pros:
- You don’t have to create dummy default values in the database
- You can still use update forms (see Toff’s answer)
Cons:
– This doesn’t protect against duplicate rows created directly at the database level.
– If you use Django’s admin backend to create new MyModel objects, you’ll need to add this same validation logic to your admin form.
- Can we have Django DateTimeField without timezone?
- How to give initial value in modelform
- Set db per model in django
- Django OneToOneField with possible blank field