如何通过ManyToMany字段

时间:2017-10-04 14:18:53

标签: python django django-models django-orm

最简单的例子:

class User(models.Model):
    name = ...

class Group(models.Model):
    members = models.ManyToManyField(User, through='GroupMembership')

class GroupMembership(models.Model):
    user = ...
    group = ...

我希望得到按注释的成员字段排序的组列表。

我使用trigram搜索来过滤和注释用户查询集。 要获得带注释的用户,我有类似的东西:

User.objects.annotate(...).annotate(similarity=...)

现在我正在尝试按用户排序群组查询集' "相似性":

ann_users = User.objects.annotate(...).annotate(similarity=...)
qs = Group.objects.prefetch_related(Prefetch('members', 
    queryset=ann_users))
qs.annotate(similarity=Max('members__similarity')).order_by('similarity')

但它不起作用,因为prefetch_related在Python中进行'加入';所以我有错误:

"FieldError: Cannot resolve keyword 'members' into field."

1 个答案:

答案 0 :(得分:0)

我希望你有一个数据库函数,用于通过trigram搜索及其Django绑定来表示名称的相似性,或者你创建任何:

from django.db.models import Max, Func, Value, Prefetch

class Similarity(Func):
    function = 'SIMILARITY'
    arity = 2

SEARCHED_NAME = 'searched_name'
ann_users = User.objects.annotate(similarity=Similarity('name', Value(SEARCHED_NAME)))
qs = Group.objects.prefetch_related(Prefetch('members', queryset=ann_users))
qs = qs.annotate(
    similarity=Max(Similarity('members__name', Value(SEARCHED_NAME)))
).order_by('similarity')

主查询编译为

SELECT app_group.id, MAX(SIMILARITY(app_user.name, %s)) AS similarity
FROM app_group
LEFT OUTER JOIN app_groupmembership ON (app_group.id = app_groupmembership.group_id)
LEFT OUTER JOIN app_user ON (app_groupmembership.user_id = app_user.id)
GROUP BY app_group.id
ORDER BY similarity ASC;
-- params: ['searched_name']

这并不是你想要的标题,但结果是一样的。

注意:评估SIMILARITY函数的效率取决于数据库查询优化器。如果在一些简化的情况下原始查询的原始想法更好,那么EXPLAIN命令的查询计划将是一个有趣的答案。