Take the 2-minute tour ×

If I have a model with a ManyToManyField and I want to restrict it to instances that have a particular property, what's the best way to do this? I can do it in the form validation or in the view, but I'd like to do it closer to the model.

For example, how can I only allow instances of class B that have is_cool set to True to be associated with class A instances?

from django.db import models

class A(models.Model):
    cool_bees = models.models.ManyToManyField('B') 

class B(models.Model):
    is_cool = models.BooleanField(default=False)
share|improve this question
@okm Nooooo, you took away my McConnell spaces! And don't try to pull PEP 8 on me because it doesn't say anything about definitions, just about function calls. ;) –  aptwebapps Jul 1 '12 at 9:34

1 Answer 1

up vote 1 down vote accepted

To be closer to the model, you can use the m2m_changed signal to check if the model matches your requirements, so the code could look like this:

import django.db.models.signals

def validate(sender, instance, action, reverse, model, pk_set, **kwargs):
    if action == "pre_add":
        # if we're adding some A's that're not cool - a.cool_bees.add(b)
        if not reverse and model.objects.filter(pk__in=pk_set, is_cool=False):
              raise ValidationError("You're adding an B that is not cool")
        # or if we add using the reverse - b.A_set.add(a)
        if reverse and not instance.is_cool:
              raise ValidationError("You cannot insert this non-cool B to A")

signals.m2m_changed.connect(validate, sender=A.cool_bees.through)
share|improve this answer
Thanks. Is there a way to do it with model validation? If I raise a 'ValidationError' here I'll just have to catch it myself. Not that that is such a hardship, but I was hoping to keep things in one place. –  aptwebapps Jul 1 '12 at 10:28
m2m changes are not performed at model.save, so it would be hard to add them in model validation. However you can set a model property e.g. a.is_valid instead of raising ValidationError and to skip wrong items, then to check it later in model validation, but this way you might hide some errors... –  Tisho Jul 1 '12 at 10:42

Your Answer


