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

4 投票
3 回答
1194 浏览
提问于 2025-04-17 17:16

我正在尝试在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都不能是空的或无值,并且active = True

我知道我可以通过遍历用户列表,然后检查他们所有的事件,看看是否符合rev1rev2active的条件,最后返回这些事件,但这样会对数据库造成很大压力。有没有什么建议呢?

谢谢!

3 个回答

0

你可以试试下面这样的代码:

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

这样做会给你一个简单的用户ID列表,每个ID出现的次数就是这个用户拥有的EventItem对象的数量。

你可能还可以做得更好,把这个结果整合到一个查询中,使用.annotate(),不过我现在不太确定具体怎么做。

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')

这个查询会返回一个用户列表,按照他们参与的事件数量从多到少排序。

想了解更多信息,可以查看 多对多 字段的相关内容。

3

我想创建一个查询,结果是一个用户列表,这些用户参加的事件数量最多,并且他们所有的相关事件项(EventItems)都满足rev1和rev2不为空或不为null,并且active为真。

首先,你需要的是那些总是包含这种类型的事件项(EventItem)的事件(Event)对象。

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='')

另外,你没有说明是否想处理那些没有事件项(EventItem)的事件(Event)对象。你可以用以下方法把这些过滤掉:

events = events.exclude(eventitem__isnull=True)

注意,events可能会包含很多重复的项。如果你想让结果更易读,可以加上events.distinct(),但只有在需要的时候才这样做。

一旦你得到了这些事件,你现在可以提取你想要的用户(User)对象:

users = User.objects.filter(event__in=events)

注意,在某些数据库后端,比如说MySQL,你可能会发现.filter(field__in=QuerySet)的方式会非常慢。对于这种情况,代码应该是:

users = User.objects.filter(event__in=list(events.values_list('pk', flat=True)))

然后你可以根据附加的事件(Event)对象的数量来排序:

from django.db.models import Count
active_users = users.annotate(num_events=Count('event')).order_by('-num_events')

撰写回答