【问题标题】:Filter django for objects containing subsets of same many to many relationship为包含相同多对多关系子集的对象过滤 django
【发布时间】:2019-03-28 05:58:32
【问题描述】:

在我的数据库中,我的用户对象上有两个多对多字段(消息和关注),它们都包含与另一个对象主题相关的多对多字段。

class User():
    messages = ManyToManyField('Message', related_name='users', blank=True, null=True)
    following = ForeignKey('Following', related_name='users', blank=True, null=True) 

class Message():
    date = DateField(blank=True, null=True)
    content = TextField(blank=True, null=True)
    topics = ManyToManyField('Topic', related_name='messages', blank=True, null=True)

class Following():
    name = CharField(max_length=255, blank=True, null=True)
    description = CharField(max_length=255, blank=True, null=True)
    topics = ManyToManyField('Topic', related_name='following', blank=True, null=True)

class Topic():
    name = CharField(max_length=255, blank=True, null=True)
    source = CharField(max_length=255, blank=True, null=True)

我想过滤所有附加了“消息”的“用户”,这些“消息”不包含附加到用户“关注”对象的所有主题。

现在我正在使用循环来完成此操作:

users = set()
for user in User.objects.filter(messages__isnull=False, following__isnull=False).iterator():
    if not set(user.following.values_list('topics', flat=True))
               ).issubset(set(user.messages.values_list('topics', flat=True)):
       users.add(user.pk)

有没有办法用一个查询来完成同样的事情?

---- 编辑----

我拥有的是这样的:

User.objects.filter(following__isnull=False
).annotate(following_count=Count('following__topics', distinct=True)
).filter(following__topics__exact=F('message__topics')
).annotate(missing_topics=ExpressionWrapper(
    F('following_count') - Count('message__topics', distinct=True),
                                 IntegerField())
).filter(missing_topics__gt=0)

如果有更好的方法可以做到这一点,或者有理由我绝对不应该这样做,它们是什么?

---- 编辑----

This question帮助我理解和使用Håken Lid's answer

这是我的新模型和我的新查询:

class User():
    messages = ManyToManyField('Message', related_name='users', blank=True, null=True)
    following = ManyToManyField('Topic', through='Following', related_name='users', blank=True, null=True) 

class Message():
    date = DateField(blank=True, null=True)
    content = TextField(blank=True, null=True)
    topics = ManyToManyField('Topic', related_name='messages', blank=True, null=True)

class Following():
    name = CharField(max_length=255, blank=True, null=True)
    description = CharField(max_length=255, blank=True, null=True)
    user = ForeignKey('User', related_name='following', blank=True, null=True)
    topic = ForeignKey('Topic', related_name='following', blank=True, null=True)

class Topic():
    name = CharField(max_length=255, blank=True, null=True)
    source = CharField(max_length=255, blank=True, null=True)



User.objects.filter(~Q(messages__topics__in=F('following'))
).values('id').annotate(missing_topics=Count('following__topics', distinct=True))

【问题讨论】:

  • 可以分享一下相关模型吗?
  • 我添加了相关模型。
  • 我不确定如何在单个查询中执行此操作。但是,如果您的用户比主题多(例如 stackoverflow),您可以尝试循环遍历每个主题而不是每个用户以提高性能。

标签: django django-queryset python-3.5


【解决方案1】:

这应该可以使用子查询来实现。

首先,确保Following.topics 使用与Messages.topics 不同的相关名称。

class Following(models.Model):
    topics = ManyToManyField('Topic', related_name='following')

那么应该可以创建subquery。像这样的:

from django.db.models import OuterRef, Subquery
user_following_topic = Topic.objects.filter(following__users=OuterRef('pk'))
User.objects.exclude(messages__topics__in=Subquery(user_following_topics.values('pk')))

这可能无法为您提供与书面完全一致的预期输出,但我认为该原则也应该适用于您的情况。

另一方面,我不太了解您的数据库结构。您似乎使用 m2m 关系,其中外键可能更合适和更简单。您的关系越复杂,创建这种高级查询就越困难。与简单查询相比,具有大量数据库连接的查询可能会非常慢,因为它们可能需要处理大量数据。

例如,Following 对我来说更有意义,而不是使用 m2m 现实:

class Following():
    topic = ForeignKey('Topic', on_delete=models.CASCADE)
    user = ForeignKey('User', on_delete=models.CASCADE)
    client = models.CharField(max_length=255, blank=True, null=True)
    duration = fields.DateRangeField(blank=False, null=False)

所以基本上是一个“通过”模型,正如在 django docs on model relationships 中解释的那样,那里有一个类似的例子。

【讨论】:

  • 我想如果我调用“Following”会更清楚:“FollowingGroup”。我试图创建一个用户组的关联,这些用户组都遵循以下对象上的相同预定义主题集。在您的示例中,我必须为每个用户为他们关注的每个主题创建一个关注对象,而我试图创建一组用户关注的主题组。这可能是您对我使用直通模型的意思?我会通过以下主题设置多对多?
  • 好吧。 m2m 的工作方式,每个 m2m 关系都会产生一个额外的表。使用“通过”,您可以向该表添加额外的列。而且由于它是一个 django 模型,您可以为模型和管理器/查询集添加自定义方法。
  • 我不明白您所说的“一组用户关注一组主题”是什么意思。组织数据库和关系没有正确的方法,但保持尽可能简单有很多好处。你的模型代码只有 14 行代码,但会产生 8 个数据库表。
  • 如果我正确理解了您对子查询的使用,那将返回所有用户的消息,其中包含用户关注的主题。我正在寻找消息中包含用户关注的所有主题的所有用户。
  • 也许我的问题措辞很糟糕。我今天发现了另一个问题,我的问题可能是重复的。 stackoverflow.com/questions/22138623/…
猜你喜欢
  • 2020-06-18
  • 2015-08-08
  • 2020-06-16
  • 1970-01-01
  • 2017-04-26
  • 2011-05-29
  • 2014-12-03
  • 2014-04-04
  • 2018-09-09
相关资源
最近更新 更多