简体   繁体   English

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

[英]Django filter queryset on “tuples” of values for multiple columns

Say I have a model:说我有一个模型:

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

and say I have a list of 2 first names: first_list = ['Bob', 'Rob'] And I have a list of 2 last names: last_list = ['Williams', 'Williamson'] .并说我有一个包含 2 个名字的列表: first_list = ['Bob', 'Rob']我有一个包含 2 个姓氏的列表: last_list = ['Williams', 'Williamson'] Then if I wanted to select everyone whose first name was in first_list I could run:然后,如果我想选择名字在first_list每个人,我可以运行:

Person.objects.filter(firstname__in=first_list)

and if I wanted to select everyone whose last name was in last_list , I could do:如果我想选择姓氏在last_list每个人,我可以这样做:

Person.objects.filter(lastname__in=last_list)

So far, so good.到目前为止,很好。 If I want to run both of those restrictions at the same time, that's easy...如果我想同时运行这两个限制,那很容易...

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

If I wanted to do the or style search instead of the and style search, I can do that with Q objects:如果我想做or样式搜索而不是and样式搜索,我可以用Q对象来做:

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

But what I have in mind is something a bit more subtle.但我想到的是更微妙的东西。 What if I just want to return a queryset that returns specific combinations of first and last names?如果我只想返回一个返回名字和姓氏特定组合的查询集怎么办? Ie I want to return the Person objects for which (Person.firstname, Person.lastname) is in zip(first_names, last_names) .即我想返回(Person.firstname, Person.lastname)zip(first_names, last_names)Person对象。 Ie I want to get back anyone named the Bob Williams or Rob Williamson (but not anyone named Bob Williamson or Rob Williams).即我想找回任何名为 Bob Williams 或 Rob Williamson 的人(但不是任何名为 Bob Williamson 或 Rob Williams 的人)。

In my actual use case, first_list and last_list would both have ~100 elements.在我的实际用例中, first_listlast_list都有~100 个元素。

At the moment, I need to solve this problem in a Django app.目前,我需要在 Django 应用程序中解决这个问题。 But I am also curious about the best way to handle this in a more general SQL context.但我也很好奇在更一般的 SQL 上下文中处理这个问题的最佳方法。

Thanks!谢谢! (And please let me know if I can clarify anything.) (如果我能澄清任何事情,请告诉我。)

I don't see much solutions except for a big OR clause:除了一个大的 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)

bruno's answer works, but it feels dirty to me - both on the Python level and on the SQL level (a large concatenation of ORs). bruno 的回答有效,但对我来说感觉很脏——无论是在 Python 级别还是 SQL 级别(大量的 OR 串联)。 In MySQL at least, you can use the following SQL syntax:至少在 MySQL 中,您可以使用以下 SQL 语法:

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

Django's ORM doesn't provide a direct way to do this, so I use raw SQL: Django 的 ORM 没有提供直接的方法来做到这一点,所以我使用原始 SQL:

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

(This is a list with one element, matching the single %s in the query. The element is an iterable of tuples, so the %s will be converted to an SQL list of tuples). (这是一个包含一个元素的列表,与查询中的单个 %s 匹配。该元素是元组的可迭代对象,因此 %s 将被转换为元组的 SQL 列表)。

Notes:注意事项:

  1. As I said, this works for MySQL.正如我所说,这适用于 MySQL。 I'm not sure what other backends support this syntax.我不确定其他哪些后端支持这种语法。
  2. A bug in python-mysql, related to this behavior, was fixed in November 2013 / MySQLdb 1.2.4, so make sure your Python MySQLdb libraries aren't older than that. python-mysql 中与此行为相关的错误已于 2013 年 11 月 / MySQLdb 1.2.4 修复,因此请确保您的 Python MySQLdb 库不早于此。

Using python 3.5 version :使用 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM