Since Django 4.1, constraints are supposedly checked during model validation*.
I therefore expected a ValidationError
for a UniqueConstraint
on a model, when I try to create a new entry through a standard CreateView
(not using a custom Form
) that violates the constraint. I was hoping that the create form would be presented again, adorned with an error message, just like a violation of the unique=True
setting of a model field does.
However, I get an IntegrityError
instead (resulting in a 500 Internal Server Error), implying that the validation never took place and the constraint was only found to be violated on database creation.
Is there something I'm doing wrong?
The Model looks something like this:
class Child(models.Model):
name = models.CharField(max_length=100)
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
class Meta:
indexes = [models.Index(fields=['name'])]
constraints = [models.UniqueConstraint(fields=['name', 'parent'], name='unique_ChildName')]
(* Older versions didn't do this, expect for the UniqueConstraint
constraint under specific circumstances, and there are other StackOverflow questions for doing this yourself, e.g.
- Django unique constraint + form errors
- How to validate uniqueness constraint across foreign key (django)
- UniqueConstraint in django 2.2 don't raise ValidationError
- django: ValidationError with UniqueConstraint on Model.Forms, clean() .
But that situation has changed since Django 4.1 and my question is specifically for Django 4.1+)
Edit: based on Willem Van Onsem's comment, I have added the parent
field to the form fields. But in order to have it be immutable, I made the Form look like this:
class ChildCreateForm(ModelForm):
parent = ModelChoiceField(queryset=None, disabled=True, required=False)
class Meta:
model = Child
fields = ['parent', 'name']
def __init__(self, parent_id=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['parent'].queryset = Parent.objects.filter(pk=parent_id)
self.fields['parent'].initial = parent_id
and this on the View:
form_class = ChildCreateForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['parent_id'] = ... # some project-specific logic
return kwargs