有没有一种优雅的方式来模拟和表示Django上多对多关系中的一对多?

时间:2013-03-16 19:39:44

标签: python django grouping django-queryset

我有点被一些似乎并不复杂的东西所阻挡。这是我的模特:

class Team(models.Model):
    name = models.CharField('team name', max_length=200, unique=True)

class QualityStream(models.Model):
    name = models.CharField('quality stream name', max_length=200, unique=True)
    team = models.ManyToManyField(Team)

class Milestone(models.Model):
    name = models.CharField('milestone name', max_length=200)
    quality_stream = models.ForeignKey(QualityStream)
    team = models.ForeignKey(Team)

正如您所看到的,团队可以拥有多个预定义的质量流,并且可以实现几个确实属于质量流的里程碑。

我的观点在这里:

<p>{{team.name}}</p>

{% for stream in team.qualitystream_set.all %}
    <p>{{stream.name}}</p>
    {% for milestone in team.milestone_set.all %}

        {% if milestone.quality_stream.id == stream.id  %}
           <p>{{milestone.name}}</p>               
        {% endif %}

    {% endfor %}    
{% endfor %}

我们的想法是展示团队,与团队相关的质量流以及按质量流分组的每个里程碑:

Team

  Quality Stream 1
     Milestone 1 for Quality Stream 1
     Milestone 2 for Quality Stream 1
  Quality Stream 2
     Milestone 1 for Quality Stream 2
     Milestone 2 for Quality Stream 2

代码运行良好,但通过循环遍历每个Quality流的所有里程碑,我感觉不太舒服。我想必须有更好的方法来实现这一目标。有什么想法吗?

2 个答案:

答案 0 :(得分:2)

您正在做的事情没有错,但可以通过在视图中执行查询来进行一些数据库优化,尤其是使用select_related():

https://docs.djangoproject.com/en/1.5/topics/db/optimization/#retrieve-everything-at-once-if-you-know-you-will-need-it

此外,如果您的模型有一堆字段,但您只需要一些字段,则只能使用()或defer()。我已经看到使用延迟来避免大字段的显着性能提升。 only()只是defer()的反转:

https://docs.djangoproject.com/en/1.5/ref/models/querysets/#defer

你的模型看起来不太复杂,所以我认为使用select_related()会对你有用。我强烈建议您使用Django debug toolbar进行优化决策。调试工具栏将显示实际的select语句以及每个语句所花费的时间。在花费太多时间进行优化之前,了解是否确实存在问题,这很有用。

希望这有帮助

答案 1 :(得分:2)

每当你认为你必须像你一样做多个下行循环时,想一想你是否可以从另一端解决问题。

正如@Pathetique所指出的,以下链接显示了如何最大限度地减少您正在执行的查询数量:

https://docs.djangoproject.com/en/1.5/topics/db/optimization/#retrieve-everything-at-once-if-you-know-you-will-need-it

该链接中的重点是select_related仅适用于ForeignKeys,而prefetch_related仅适用于ManyToManyFields

由于您从“我有一组团队,现在我想要显示与这些团队相关的所有数据”的角度来处理这个问题,因此您无法使用自{{{}以来可用的任何优化。 1}}没有TeamForeignKeys

相反,您可以像这样处理问题“我希望显示所有里程碑,按团队分组,然后按质量流分组”。由于您的Milestone类可以访问您需要的所有数据,因此它使查询集非常容易构造,只生成一个查询。

ManyToManyFields

现在,您的模板将打破您构建它的方式。这就是我之前提到的问题的定义所在。您拥有所有里程碑,但是您希望按团队分组,然后按质量流分组。我们的查询集的排序现在可以帮助我们,因为我们可以遍历所有里程碑,并检查我们是否需要新的团队或新的质量流。

def my_view(request):
    queryset = Milestone.objects.select_related(
        'team', 'quality_stream'
    ).order_by(
        'team__name', 'quality_stream__name'
    ) # a single query to fetch all teams + streams + milestones

    return render_to_response('/your/template.html', 
        { 'milestones':queryset },
        context_instance=RequestContext(request)
    )

上述模板使用ifchanged模板标记,这似乎是为此目的而设计的。