[Answer]-Django – Relate model X to max n instances of Model Y

1👍

In my opinion, you should look into django models’ clean() method.
It’s called when you try to save an instance. For your case, the code could look like this (untested):

from django.core.exceptions import ValidationError


class Person(models.Model):
    max_books = 1

    def clean(self):
        books_count = self.books.all().count()
        if books_count >= max_books:
            raise ValidationError("This person has too much books !")


 class Book(models.Model):
    issued_to = models.ForeignKey(Person, related_name="books")

This way, you can create subclass of Person model, and set your own max_books limit:

class Student(Person)
    max_books = 3

class Employee(Person)
    max_books = 30

However, be careful because, by using this solution, you will rely on django model’s concrete inheritance, which can cause performance issues.

👤Agate

0👍

Another solution might be to use django signals.

Django will send a pre_save signal before saving any model, so you can hook a function to react to it and perform your checks.

something along the lines of (building off @Agate solution):

from django.db.models.signals import pre_save
from django.core.exceptions import ValidationError

def validate_num_books(sender, **kwargs):
    if isinstance(sender, Student):
        max_books = 3
    elif isinstance(sender, Employee):
        max_books = 30

    books_count = sender.books.all().count()
    if books_count >= max_books:
        raise ValidationError("This person has too much books !")

pre_save.connect(validate_num_books, sender=Employee, dispatch_uid='validate_num_books')
pre_save.connect(validate_num_books, sender=Student, dispatch_uid='validate_num_books')

Notes:

  1. I’m not sure the ValidationError will have the intended effect. Hopefully it’ll be enough to bypass the model being saved, but maybe it doesn’t work quite that way, or it does in some situations or but not in others… Consider this snippet as experimental pseudo code.

  2. As you might have guessed from the point above, i’m not that used to signals, and still a bit weary of those. This might be completely fine, but if a cleaner solution arises, i’d probably go for it and avoid signals entirely. Simpler is usually better.

  3. The only advantage i can see of this solution over Eliot’s answer is that it avoids inheritance, which, as he said, can cause performance trouble. However, if you avoid defining fields on the parent class, I think you should be fine.

If you decide to experiment with signals, start here.

Leave a comment