Reputation: 2197
Question is regarding usage of custom function in check constraint:
I have a following model:
class UserIP(models.Model):
user = models.ForeignKey(
to=User,
on_delete=models.CASCADE,
related_name='user_ip',
)
ip = models.GenericIPAddressField(
verbose_name='User ip address'
)
sample_time = models.DateTimeField(
auto_now=True,
verbose_name='User ip sample time'
)
and I have following custom function in database:
create or replace function count_ips(v_ip inet , v_user_id int, v_limit int)
returns boolean as $$
select count(*) > v_limit
from users_userip
where ip = v_ip and user_id = v_user_id
$$ language sql;
which returns True
if there are more then X (meant to be 3) entries in DB with same ip
and user
.
Based on this function I created Django function like this:
class IpCount(Func):
function = 'count_ips'
arity = 3
output_field = BooleanField()
usage example:
UserIP.objects.all().annotate(ann=IpCount(Value('127.0.0.1'), 1,3)).first().ann
works perfect
Now I want to make a check constraint that would not allow to save in DB any new entry if there are already 3 or more entries in DB where user
and ip
are the same.
constraints = [
models.CheckConstraint(
name='max_3_ips',
check=~models.Q(IpCount('ip', 'user_id', 3)),
), ]
It says then since Django > 3.1. it supports boolean expressions inside chekc constraints but what I have written it does not work. Error is like this:
File "C:\Users\hardcase1\.virtualenvs\series-EAOB5IHD\lib\site-packages\django\db\models\query_utils.py", line 117, in deconstruct
kwargs = {child[0]: child[1]}
TypeError: 'IpCount' object is not subscriptable
Seems like Django mirations can not serialize this function.
Question is – how to use this function in check constraint and is it possible at all or should i just forget it and create custom migration with a bunch of RAW sQL inside instead?
Thanks
Upvotes: 3
Views: 697
Reputation: 2197
Since django 3.1 it is possible to use expressions that returns boolean, so that it is no point of using Q
models.CheckConstraint(
name='max_3_ips',
check=IpCount(models.F('user_id'), models.Value(2)),
), ]
Upvotes: 2