Django many-to-many contstaints validation

590 Views Asked by At

I am trying to create a constraint that checks whether both two fields have falsy values. One of these fields is a boolean, and the other is a m2m as in below:

class Test(models.Model):
    public = models.BooleanField(default=False)
    target_groups = models.ManyToManyField("TargetGroup", blank=True)

    class Meta:
        constraints = [
            models.CheckConstraint(
                name="%(app_label)s_%(class)s_check_public_or_target_groups",
                check=Q(public=False, target_groups__isnull=True)
            )
        ]

This gets me 'constraints' refers to a ManyToManyField 'target_groups', but ManyToManyFields are not permitted in 'constraints'.

Is there anyway I can check that either public is True or target_groups is not empty when creating/ updating? I checked this and this.

I tried for example validating on the save method as in the following:

def save(self, *args, **kwargs):
    
    if self.public is False and not self.target_groups.exists():
        raise ValidationError(
            {"public": _("Notification requires either setting public boolean to true, or providing target groups.")}
        )
    return super().save(*args, **kwargs)

But the condition for self.target_groups is always false which I think makes sense since the object is not added to the set yet, but how do I validate the passed in data from the request? I use DRF and I can already validate this on serializers, but admins can add this through Django admin as well, so I am trying to validate this on the model level.

I appreciate any insights.

1

There are 1 best solutions below

0
On

The django admin makes the changes to a ManyToMany field separately from changing the actual object.

Remember that the m2m is saved in a different database table.

from django.contrib import admin

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        # wrire your code here
        super().save_model(request, obj, form, change)

You can refer to documentions