【问题标题】:Django ManyToManyField query equalivent to a listDjango ManyToManyField 查询相当于一个列表
【发布时间】:2020-03-30 17:37:45
【问题描述】:

我有一个对象列表,一个模型包括该对象的 ManyToManyField。 我想获取在该字段中具有相同对象列表的对象。 这意味着 __in 将不起作用,因为他将在列表中的对象之间执行 OR 而不是 AND。 我正在尝试使用 AND Q 查找,但它不起作用(在检查 .query 文本之后,似乎他在字段对象内部执行 AND,而不是对象本身,所以显然字段对象 id 不有两个不同的 id..)

这是我玩过的拦截器

results_query_set[0]
<Results: Results object (2)>
results_query_set[0].users.all()
<QuerySet [<User: test_user>, <User: test_user_2>]>
users
[<User: test_user>, <User: test_user_2>]
users_q
<Q: (AND: ('users', <User: test_user>), ('users', <User: test_user_2>))>
results_query_set.filter(users=users[0])
<QuerySet [<Results: Results object (2)>]>
results_query_set.filter(users=users[1])
<QuerySet [<Results: Results object (2)>]>
results_query_set.filter(users_q)
<QuerySet []>
results_query_set.filter(Q(users=users[0]) & Q(users=users[1]))
<QuerySet []>

结果results_query_set.filter(users_q).query.__str__()重现是

'SELECT "results_table"."id", "results_table"."date", "results_table"."lookup", "results_table"."value" FROM "results_table" INNER JOIN "results_table_users" ON ("results_table"."id" = "results_table_users"."widgetresults_id") WHERE ("results_table_users"."user_id" = 1 AND "results_table_users"."user_id" = 2)

我可以为每个用户链接 .filter,但当然,我想进行一次查询,而不是根据输入的数字进行查询。

【问题讨论】:

  • 你用的是什么数据库?
  • @schillingt 抱歉,编辑了标签。 (postgres)

标签: django python-3.x postgresql django-models


【解决方案1】:

您需要多次(针对每个用户)JOIN 目标表(在您的情况下为User)才能构建这样的查询。

在 Django 中执行此操作的方法是多次调用.filter。

users = [user1, user2] # a list of users your are interested to filter on
initial_qs = Results.objects.all() # or whatever your results_query_set is
result_qs = reduce(lambda qs, user: qs.filter(users=user.id), users, initial_qs)

# At this point you will have results containing user1 and user2
# but this also includes results with more users (e.g. users 1, 2 and 3)
# if you want to exclude those, you need to filter by the total users count too
result_qs = result_qs.annotate(cnt=models.Count('users')).filter(cnt=len(users))

【讨论】:

  • 是的,我就是这么想的,但想防止这种情况发生,并认为 django 可能有更好的干净解决方案。
  • 这个解决方案对我来说似乎很干净我不确定你想要它有多干净,但是再次阅读你的问题,链接/调用.filter 多次不会进行多次查询,您的查询仍然是一个,但有多个连接。如果你真的想要一个join,那么你最好的选择是用group_concat之类的东西注释ids(django默认不支持),然后过滤这个注释,但这肯定是比多个连接慢。
  • 有趣。为什么会变慢?听起来像 O(n)
  • 过滤将发生在 SQL HAVING 子句中,在获取所有记录并计算注释值之后,通过多个连接,RDBMS 确切地知道需要获取什么以及如何最大限度地优化需要获取的记录。
【解决方案2】:

我还没有尝试过,但我认为你可以使用 postgresql 的array_agg 函数。 Django 实现是here。

from django.contrib.postgres.aggregates import ArrayAgg

ideal_user_list = [] # some list. 
                     # Or fetch it directly from the db using the below query and `get`
Results.objects.annotate(
    related_user_array=ArrayAgg('users__id', ordering='users__id')
).filter(related_user_array=ideal_user_list)

【讨论】:

  • 不工作并抛出django.db.utils.ProgrammingError: type "serial[]" does not exist的异常
猜你喜欢
  • 1970-01-01
  • 2011-03-28
  • 2021-04-30
  • 2010-12-22
  • 2019-07-13
  • 1970-01-01
  • 2010-11-25
  • 2014-08-01
  • 2012-07-04
相关资源
最近更新 更多