首页 > 解决方案 > 基于自定义 SQL 和自定义 Django 函数创建检查约束

问题描述

问题是关于在检查约束中使用自定义函数:

我有以下模型:

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'
    )

我在数据库中有以下自定义函数:

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;

True如果 DB 中有超过 X(意味着 3)个具有相同ip和的条目,则返回user

基于这个函数,我创建了 Django 函数,如下所示:

class IpCount(Func):
    function = 'count_ips'
    arity = 3
    output_field = BooleanField()

用法示例:

UserIP.objects.all().annotate(ann=IpCount(Value('127.0.0.1'), 1,3)).first().ann

完美运行

user现在我想创建一个检查约束,如果 DB 中已经有 3 个或更多条目并且ip相同,则不允许在 DB 中保存任何新条目。


constraints = [
    models.CheckConstraint(
        name='max_3_ips',
        check=~models.Q(IpCount('ip', 'user_id', 3)),
    ), ]

它说从 Django > 3.1 开始。它支持 chekc 约束中的布尔表达式,但我写的它不起作用。错误是这样的:

 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

似乎 Django mirations 无法序列化此功能。

问题是——如何在检查约束中使用这个函数,有没有可能,或者我应该忘记它,而是用一堆 RAW SQL 创建自定义迁移?

谢谢

标签: djangodjango-orm

解决方案


从 django 3.1 开始,可以使用返回布尔值的表达式,因此使用 Q 没有意义

models.CheckConstraint(
                name='max_3_ips',
                check=IpCount(models.F('user_id'), models.Value(2)),
            ), ]

推荐阅读