Django QuerySets:从ForeignKey字段获取unique / first元素

时间:2013-12-11 18:50:49

标签: mysql django orm django-queryset

我有以下模特:

class SchoolClass(models.Model):
    title = models.CharField()
    max_students = models.IntegerField(default=10)
    is_published = models.BooleanField(default=False)

class Session(models.Model):
    school_class = models.ForeignKey(SchoolClass, related_name="sessions")
    start_date = models.DateField()
    enrolled_students = models.IntegerField()

一个SchoolClass可以有多个Sessions(例如,周一,周二和周五的“Dummies数学”= 3个会话)。

在我的索引页面(ListView)上,我想只显示所有即将开始的类(至少有一个会话),因为有很多会话的类会使概述变得混乱。

如何创建会话对象列表(我可以通过session.school_class获取相关课程信息),符合以下要求:

  • start_date__gte=datetime.now().date()
  • school_class.is_published=True
  • 这是SchoolClass(oder_by('start_date')[0]
  • 的第一个元素

谢谢!

PS:我正在运行MySQL作为数据库,因此不同的('field')将不起作用..


修改

以下是我当前的解决方案,它可以工作,但不是很干净(DB上的冗余命中)

def get_sessions_mixin(filter_args):
    session_qs = []
    qs = SchoolClass.objects.filter(**filter_args)
    for schoolclass in qs:
        session_qs.append(schoolclass.get_first_session())

    #session_qs = Session.objects.filter(course_meta__is_deleted=False, date_from__gte=datetime.now().date()).\
    #    annotate(Max('date_from'))
    #session_qs = qs.order_by('get_first_session__date_from')

    return session_qs

和models.py

class Sessions(models.Model):
    # ...
    def get_first_session(self):
        if self.is_published:
            tmp_sessions = self.sessions.filter(date_from__gte=datetime.now().date())
            if tmp_sessions:
            return tmp_sessions.order_by('date_from')[0]
    return None

2 个答案:

答案 0 :(得分:1)

我无法想到在单个Django ORM查询中执行所需操作的方法。我可能会做的是,首先,获取所有相关的Session对象:

sessions = Session.objects.filter(school_class__is_published=True,
                                  start_date__gte=datetime.now().date())
                          .select_related('school_class')
                          .order_by('start_date')

您现在可以从单个查询中获得所需的一切,包括相关的SchoolClass数据。您只需要在Python中做一些工作来过滤数据。有很多方法可以做到这一点,这里有一个简单的方法:

first_sessions = collections.OrderedDict()
for session in sessions:
    if session.school_class.pk not in first_sessions:
        first_sessions[session.school_class.pk] = session

现在first_sessions.values()有你想要的。您可以将其包装在自定义Manager上的方法中。

在涉及数据库时很难对性能进行猜测(特别是不知道典型Sessions有多少SchoolClass),但我怀疑这种单一查询方法会更快更简单而不是你当前的解决方案,它会多次击中数据库。

答案 1 :(得分:0)

似乎这种查询可能有效:

SchoolClass.objects.filter(sessions__start_date__gte=datetime.now().date(), is_published=True)\
    .annotate(session_count=Count('sessions'))\
    .filter(session_count__gte=1)

但我不确定第三点是什么意思。

相关问题