I am using Django 4.1 and Postgresql and as stated in their documentation CheckConstraint
accept Q
object and Expression
.
Based on https://code.djangoproject.com/ticket/31646, I thought my solution would work, but when calling makemigrations
nothing happens (Count
inherit from Func
).
Goal: I would like to limit the number of Messages per Discussion.
I did see a solution using a validators on the ForeignKey field but it is not robust (see Limit number of foreign keys). I would prefer not to have to create a SQL function and calling it (would like a Django solution only).
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),
),
),
]
CodePudding user response:
constraints
need to be in the Meta
class(look here):
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),
),
),
]
Other solutions:
Solution 1:
Each model has a save() option that is called when the model is saved. You can check here and raise an error if the user already has 10 messages.
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)
Solution 2:
Using validators:
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,))