Ограничение количества внешних ключей с помощью Django CheckConstraint

Я использую Django 4.1 и Postgresql и, как указано в их документации CheckConstraint принимают Q объект и Expression.

Основываясь на https://code.djangoproject.com/ticket/31646, я думал, что мое решение будет работать, но при вызове makemigrations ничего не происходит (Count наследуется от Func).

Цель: Я хотел бы ограничить количество сообщений в одной дискуссии.

Я видел решение с использованием валидатора на поле ForeignKey, но оно не надежно (см. Ограничение количества внешних ключей). Я бы предпочел не создавать SQL функцию и вызывать ее (хотелось бы получить решение только на Django).

from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.lookups import IntegerLessThan

class Discussion(models.Model):
    MAX_MESSAGES = 10


class Message(models.Model):
    discussion = models.ForeignKey(
        "discussion.Discussion",
        models.CASCADE,
        related_name="messages",
    )
    constraints = [
        models.CheckConstraint(
            name="limit_discussion_messages",
            check=IntegerLessThan(
                models.Count("discussion", filter=models.Q(discussion=models.F("discussion"))),
                models.Value(Discussion.MAX_MESSAGES),
            ),
        ),
    ]

constraints нужно быть в Meta классе ( смотрите здесь):

class Message(models.Model):
    discussion = models.ForeignKey(
        "discussion.Discussion",
        models.CASCADE,
        related_name="messages",
    )

    class Meta:
        constraints = [
        models.CheckConstraint(
        name="limit_discussion_messages",
        check=IntegerLessThan(
            models.Count("discussion", filter=models.Q(discussion=models.F("discussion"))),
            models.Value(Discussion.MAX_MESSAGES),
        ),
        ),
    ]

Другие решения:

Решение 1:

У каждой модели есть опция save(), которая вызывается при сохранении модели. Вы можете проверить здесь и выдать ошибку, если у пользователя уже есть 10 сообщений.

class Message(models.Model):
    discussion = models.ForeignKey("discussion.Discussion", models.CASCADE,related_name="messages")

    def save(self,*args, **kwargs):
        if self.id == None: #Creating a new object
            if Message.objects.filter(discussion=request.discussion).count() >= 10:
                #Raise whatever error you want or just return false
        return super(Post, self).save(*args, **kwargs)

Решение 2:

Использование валидаторов:

def restrict_amount(value):
        if Message.objects.filter(discussion=value).count() >= 3:
            raise ValidationError('You already have max amount of discussions (3)')
    
    class Message(models.Model):
        discussion = models.ForeignKey("discussion.Discussion",validators=(restrict_amount,))
Вернуться на верх