Search code examples
pythondjangodjango-admindjango-signalsdjango-validation

Showing validation error from m2m_changed signal in django admin


I am trying to validate that the variable foo is the same for MyModel and Item before adding it as a m2m. I want to raise a ValidationError in the admin if it is not.

models.py

class Item(models.Model):
    foo = models.CharField(max_length=200)    

class MyModel(models.Model):
    foo = models.CharField(max_length=200)
    items = models.ManyToManyField(Item)

signals.py

@receiver(m2m_changed, sender=MyModel.items.through)
def my_validator(sender, instance, action, pk_set, **kwargs):
    if action == 'pre_add':
        if Item.objects.filter(id__in=pk_set, foo=instance.foo).count() != len(pk_set):
            raise ValidationError({'items': ["Foo doesn't match"]})

Is there a way for the ValidationError to show up properly in the admin and not as a 500 error.

I wasn't able to come up with a solution to use MyModel's clean method to validate the same value of foo. Any advice is appreciated.


Solution

  • Create a form class with clean method and modify your admin class to use the form. Read this:

    Like:

    @admin.register(MyModel)
    class MyModelAdmin(admin.ModelAdmin):
        search_fields = ('foo', 'items__foo')
        list_display = ('foo', 'items__foo')
        form = MyModelForm
    
    
    class MyModelForm(forms.ModelForm):
    
        def clean(self):
            """
            This is the function that can be used to 
            validate your model data from admin
            """
            super(MyModelForm, self).clean()
            foo = self.cleaned_data.get('foo')
            pk_set = Item.objects.all().values_list("id")
    
            # The logic you were trying to filter..
            if Item.objects.filter(id__in=pk_set).count() != len(pk_set):
                raise ValidationError({'items': ["Foo doesn't match"]})