Search code examples
pythondjangodjango-modelsdjango-viewsdjango-forms

django UniqueConstraint violation_error_message message not handled


I have this model:

class DataMapping(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    location = models.ForeignKey(Location, null=True, blank=True, on_delete=models.CASCADE)
    department = models.ForeignKey(LocationDepartment, null=True, blank=True, on_delete=models.CASCADE)
    catagory = models.ForeignKey(CompanyCategory, on_delete=models.CASCADE)
    reason = models.ForeignKey(ReasonForProcessing, on_delete=models.CASCADE)

        class Meta:
            constraints = [
                UniqueConstraint(
                    name='unique_data_map', 
                    fields=['company', 'location', 'department', 'catagory', 'reason'],
                    condition=Q(location__isnull=False),
                    violation_error_message="This data map has already been added.",
                ),
                UniqueConstraint(
                    name='unique_data_map_both_none_dep_none', 
                    fields=['company', 'location', 'catagory', 'reason'],
                    condition=Q(department__isnull=True),
                    violation_error_message="This data map has already been added."
                ),
                UniqueConstraint(
                    name='unique_data_map_both_none', 
                    fields=['company', 'catagory', 'reason'],
                    condition=Q(location__isnull=True) & Q(department__isnull=True),
                    violation_error_message="This data map has already been added."
                )
    ]

with a basic modelform

class DataMapForm(forms.ModelForm):
    class Meta:
        model = DataMapping
        fields = (
            'location',
            'department',
            'catagory',
            'reason',
    )
    widgets = {
        'company': forms.HiddenInput()
    }

and a view of:

def data_mapping_new(request):
    company = get_current_logged_in_company(request)

    if request.method == "POST":
        form = DataMapForm(request.POST or None)
        if form.is_valid():
            catagory = form.save(commit=False)
            catagory.company = company
            if form.is_valid():
                form.save()

I have a similar setup on a different model that works fine, but this one just raises an error django.db.utils.IntegrityError: duplicate key value violates unique constraint "unique_data_map_both_none"

Should it not check the constraint at the first if form.is_valid():

What am I missing here?

EDIT: I used a similar pattern for a model somewhere else and it works fine there, with the only difference there being that the condition is on a char field and not a foreign key as in this model. condition=Q(email__isnull=False) & Q(email__gt=''). Maybe my issue is in how I set up the condition?


Solution

  • Ok, so after a lot of "when in doubt cout", I noticed that model forms only check constraints that include all the fields that the form includes. In my case the model included

    'location',
    'department',
    'catagory',
    'reason',
    

    but the constraint included company. I did not add company here as it is not something the user sets in the form, but it is a hidden field. Point is that the constraint did not trigger as the constraint includes company. Adding company to the fields list in the model form resolved my problem.