Django在多列值的“元组”上过滤查询集

Posted

技术标签:

【中文标题】Django在多列值的“元组”上过滤查询集【英文标题】:Django filter queryset on "tuples" of values for multiple columns 【发布时间】:2013-12-09 06:35:28 【问题描述】:

假设我有一个模型:

Class Person(models.Model):
    firstname = models.CharField()
    lastname = models.CharField()
    birthday = models.DateField()
    # etc...

并说我有一个包含 2 个名字的列表:first_list = ['Bob', 'Rob'] 我有一个包含 2 个姓氏的列表:last_list = ['Williams', 'Williamson']。然后,如果我想选择名字在first_list 中的每个人,我可以运行:

Person.objects.filter(firstname__in=first_list)

如果我想选择姓氏在last_list 中的每个人,我可以这样做:

Person.objects.filter(lastname__in=last_list)

到目前为止,一切都很好。如果我想同时运行这两个限制,那很容易......

Person.objects.filter(firstname__in=first_list, lastname__in=last_list)

如果我想进行 or 样式搜索而不是 and 样式搜索,我可以使用 Q 对象来实现:

Person.objects.filter(Q(firstname__in=first_list) | Q(lastname__in=last_name))

但我想到的是一些更微妙的东西。如果我只想返回一个返回名字和姓氏特定组合的查询集怎么办? IE。我想返回(Person.firstname, Person.lastname)zip(first_names, last_names) 中的Person 对象。 IE。我想找回任何名为 Bob Williams 或 Rob Williamson 的人(但不是任何名为 Bob Williamson 或 Rob Williams 的人)。

在我的实际用例中,first_listlast_list 都有大约 100 个元素。

目前,我需要在 Django 应用程序中解决这个问题。但我也很好奇在更一般的 SQL 上下文中处理此问题的最佳方法。

谢谢! (如果我能澄清任何事情,请告诉我。)

【问题讨论】:

另一个问题有一个很好的解决方案:***.com/a/41717889/6762200 【参考方案1】:

使用python 3.5版本:

import operator
import functools

query = functools.reduce(
    operator.or_, 
    (Q(firstname=fn, lastname=ln) for fn, ln in zip(first_list, last_list))
    )

Person.objects.filter(query)

【讨论】:

【参考方案2】:

bruno 的答案有效,但我觉得它很脏——无论是在 Python 级别还是在 SQL 级别(OR 的大量串联)。至少在 mysql 中,您可以使用以下 SQL 语法:

SELECT id FROM table WHERE (first_name, last_name) IN
       (('John','Doe'),('Jane','Smith'),('Bill','Clinton'))

Django 的 ORM 没有提供直接的方法来做到这一点,所以我使用原始 SQL:

User.objects.raw('SELECT * FROM table WHERE (first_name, last_name) IN %s',
      [ (('John','Doe'),('Jane','Smith'),('Bill','Clinton')) ])

(这是一个包含一个元素的列表,与查询中的单个 %s 匹配。该元素是元组的可迭代对象,因此 %s 将转换为元组的 SQL 列表)。

注意事项:

    正如我所说,这适用于 MySQL。我不确定还有哪些其他后端支持这种语法。 python-mysql 中与此行为相关的错误已在 2013 年 11 月 / MySQLdb 1.2.4 中修复,因此请确保您的 Python MySQLdb 库不早于此。

【讨论】:

【参考方案3】:

除了一个大的 OR 子句之外,我没有看到太多解决方案:

import operator
from itertools import izip
query = reduce(
    operator.or_, 
    (Q(firstname=fn, lastname=ln) for fn, ln in izip(first_list, last_list))
    )

Person.objects.filter(query)

【讨论】:

我没有意识到您可以以编程方式组合 Q 这样的语句!这看起来很完美。从 SQL 的角度来看,你知道这种查询是否有效吗? 生成的 SQL 将与您手动编写的 SQL 相同:SELECT <fieldnames here...> FROM <tablename> WHERE (fistname="X1" AND lastname="y1") OR (firstname="x2" AND lastname="Y2") OR <etc....>。为了提高效率,您可能希望在(名字,姓氏)上添加索引,在具有真实数据的大型数据集上应该具有足够的区分能力以加快速度(假设您的数据库服务器足够智能使用它),但首先按原样尝试并检查它是否真的需要任何优化。 哦,如果您知道另一种编写 SQL 查询并获得相同结果的方法,请告诉我,我总是愿意学习新事物。 我自己是一个 SQL 新手。想到的另一种通用方法是在查询本身中动态创建合并列:SELECT <fieldnames> FROM <tablename> WHERE CONCAT(firstname, lastname) IN <list of concatted first and last names>。 (编辑:我认为您的方法可能会更快,因为设计良好的 SQL 引擎应该首先过滤可接受的名字或可接受的姓氏,然后检查这些对是否正常,而我的需要为每一行制作 concatted col) .好奇其他人必须添加什么! @DJ_8one6:无论如何,我都不是 SQL 专家,但我对 SQL 数据库的经验就像使用 C 编译器一样,如果你使用好的编译器并且知道正确的标志/索引/任何技巧,您将很难“手动”获得更好的优化,因此最好编写好的但简单的代码并让数据库引擎/编译器完成这项工作(只需一点提示)。我的 2 美分...

以上是关于Django在多列值的“元组”上过滤查询集的主要内容,如果未能解决你的问题,请参考以下文章

为了在 Django 中过滤数据,为多列构建动态查询

构建请求 URL 以通过同一字段多次过滤 Django 查询集

包含列表中所有值的 Django 反向查询集

通过具有最大列值的记录过滤 Django 查询

如何使用 SQL 的“IN”等字段上的数组过滤 django 查询集?

如何使用 SQL 的“IN”等字段上的数组过滤 django 查询集?