使用Django ORM编写元组搜索

6qqygrtg  于 2023-03-04  发布在  Go
关注(0)|答案(3)|浏览(136)

我尝试用DjangoORM语法编写一个基于元组的搜索。
最后的sql语句应类似于:

SELECT * FROM mytable WHERE (field_a,field_b) IN ((1,2),(3,4));

我知道我可以在django中使用额外的关键字来实现这一点:

MyModel.objects.extra(
    where=["(field_a, field_b) IN %s"],
    params=[((1,2),(3,4))]
)

但是“extra”关键字在django中的某个时候会被弃用,所以我想要一个纯粹的ORM/django解决方案。
在网上搜索,我找到了https://code.djangoproject.com/ticket/33015和Simon Charette的评论,下面的代码片段可能是好的,但我不能让它工作。

from django.db.models import Func, lookups

class ExpressionTuple(Func):
    template = '(%(expressions)s)'
    arg_joiner = ","

MyModel.objects.filter(lookups.In(
    ExpressionTuple('field_a', 'field_b'),
    ((1,2),(3,4)),
))

我使用的是Django3.2,但我不期望Django4.x会有什么大的不同。我的数据库后端是posgresql。

a0x5cqrl

a0x5cqrl1#

我可以想到一个解决方案,它将使用Q预先构建查询,然后将其传递给filter函数:

q = Q()
for (item1, item2) in [(1,2),(3,4)]:
    q |= Q(field_one=item1, field_two=item2)

Mymodel.objects.filter(q)

另一个更健壮的解决方案如下所示:

q = Q()

fields = ['field_one', 'field_two']

for item in [(1,2),(3,4)]:
   q |= Q(**dict(zip(fields, item)))

在这里,我压缩了项目列表中的字段和项目,然后将其作为解压缩字典传递给Q。这是一个与上一个示例类似的实现,但这里的字段数量可以很多,但不会增加代码中的行数。

esyap4oy

esyap4oy2#

from django.db.models import Func, lookups

class Tuple(Func):
    function = '(%s)'

    def as_sql(self, compiler, connection):
        sql, params = super().as_sql(compiler, connection)
        if sql.endswith(',)'):
            sql = sql[:-2] + ')'
        return sql, params

MyModel.objects.filter((Func('field_a', function='(%s)'), Func('field_b', function='(%s)'))__in=[(1,2),(3,4)])

经过这些更改后,生成的SQL查询应该类似于:

SELECT * FROM mytable WHERE (field_a, field_b) IN ((1, 2), (3, 4))
pftdvrlh

pftdvrlh3#

作为参考和灵感来自akshay-jain的建议,我设法写一些作品:

from django.db.models import Func,Value

def ValueTuple(items):
    return tuple(Value(i) for i in items)

class Tuple(Func):
    function = ''

qs = (
    MyModel.objects
    .alias(a=Tuple('field_a', 'field_b'))
    .filter(a__in=ValueTuple([(1, 2), (3, 4)])
)

它会生成一个sql查询,如

SELECT * FROM table WHERE (field_a,field_b) IN ((1,2),(3,4));

并且可以扩展到比仅仅两个更多的领域。
我没有做任何基准比较它与Q对象过滤虽然。

相关问题