Ограничение количества внешних ключей с помощью 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,))