最有效的Django查询返回跨越多个表的结果

时间:2013-02-27 05:57:55

标签: python django

我试图以最有效的方式在Django中进行一个非常复杂的查询,我不知道如何开始。我有这些模型(这是一个简化版本)

class Status(models.Model):
    status = models.CharField(max_length=200)

class User(models.Model):
    name = models.CharField(max_length=200)

class Event(models.Model):
    user = models.ForeignKey(User)

class EventItem(models.Model):
    event = models.ForeignKey(Event)
    rev1 = models.ForeignKey(Status, related_name='rev1', blank=True, null=True)
    rev2 = models.ForeignKey(Status, related_name='rev2', blank=True, null=True)
    active = models.BooleanField()

我想创建一个查询,该查询将生成一个具有最多事件的用户列表,其中所有相关的EventItems rev1rev2都不是空白或nulland active = True

我知道我可以通过迭代用户列表,然后检查所有事件以查找匹配的rev1rev2active条件,然后返回这些事件,但这对数据库来说很重要。有什么建议吗?

谢谢!

3 个答案:

答案 0 :(得分:6)

您的模型已被破坏,但这应该以更清洁的方式总结您正在做的事情。

class Status(models.Model):
    status = models.CharField(max_length=200)

class User(models.Model):
    name = models.CharField(max_length=200)
    events = models.ManyToManyField('Event')

class Event(models.Model):
    rev1 = models.ForeignKey(Status, related_name='rev1', blank=True, null=True)
    rev2 = models.ForeignKey(Status, related_name='rev2', blank=True, null=True)
    active = models.BooleanField()

和查询

User.objects.filter(events__active=True).exclude(Q(events__rev1=None)|Q(events__rev2=None)).annotate(num_events=Count('events')).order_by('-num_events')

这将返回一个用户列表,按其集合中的事件数排序。

有关详细信息,请查看Many-To-Many字段。

答案 1 :(得分:3)

  

我想创建一个查询,该查询将生成一个具有最多事件的用户列表,其中所有依赖的EventItem都具有rev1和rev2不为空或null且active = True。

首先,您希望Event个对象始终具有此类EventItem

events = Event.objects.filter(active=True)
events = events.exclude(eventitem__rev1__isnull=True)
events = events.exclude(eventitem__rev1='')
events = events.exclude(eventitem__rev2__isnull=True)
events = events.exclude(eventitem__rev2='')

此外,您没有指定是否要处理没有Event的{​​{1}}个对象。你可以用以下方法过滤掉这些:

EventItem

请注意,events = events.exclude(eventitem__isnull=True) 可能包含大量重复项。如果你愿意,你可以投入一个events,但只有在你需要它时才能这样做。

有了这些,您现在可以提取所需的events.distinct()个对象:

User

请注意,在某些数据库后端* ahem * MySQL * ahem *上,您可能会发现users = User.objects.filter(event__in=events) 模式为really slow。对于这种情况,代码应为:

.filter(field__in=QuerySet)

然后,您可以按照附加的users = User.objects.filter(event__in=list(events.values_list('pk', flat=True))) 个对象的数量来订购:

Event

答案 2 :(得分:0)

您可以尝试以下内容:

EventItem.objects.exclude(rev1=None).exclude(rev2=None).filter(active=True).values_list('event__user', flat=True)

这将为您提供一个用户ID的平面列表,其中每个id的频率是用户拥有的EventItem对象的数量。

您可以做得更好,并使用.annotate()将其整合到查询中,但我不知道现在该怎么做。