【问题标题】:Django and models with multiple foreign keysDjango 和具有多个外键的模型
【发布时间】:2012-03-16 00:46:46
【问题描述】:

我是 Django 新手,到目前为止,我对它的功能印象深刻。我正在玩更复杂的模型,但我无法正确使用它们。使用 Django 1.3,我正在尝试编写一个摘要页面,该页面将显示以下具有以下结构的三个模型。换言之,包含目的地和活动的旅行列表。

  • 行程 1
    • 目的地 1
    • 目的地 2
    • 活动 1
  • 旅行 2
    • 目的地 1
    • 活动 2

模型

  • 旅行 TripDestination 目的地(一次旅行可以有多个目的地)
  • 活动 -> 旅行、活动 -> 目的地(活动是为在特定位置/目的地的旅行定义的)
    class Destination(models.Model):
        city_name=models.CharField()

    class Trip(models.Model):
        departing_on=models.DateField()
        returning_on=models.DateField()
        destinations=models.ManyToManyField(Destination)

    class Activity(models.Model):
        destination=models.ForeignKey(Destination, null=False)
        trip=models.ForeignKey(Trip, null=False)

我正在尝试编写一个视图,该视图将生成一个具有上述结构的页面。我现在遇到的主要问题是显示特定旅行和目的地的活动。正如您在下面的代码中看到的那样,我正在构建一个字典,我怀疑这是正确的做法。另外,视图变成了

查看

def list_trip(request, template_name = 'trip-list.html'):
    trips = Trip.objects.all()

    # Build a dictionary for activities -- Is this the right thing to do?
    activities = Activity.objects.filter(trip__in=trips)
    activities_by_trips = dict()
    for activity in activities:
        if activity.trip_id not in activities_by_trips:
            activities_by_trips[activity.trip_id] = dict()

        if activity.destination_id not in activities_by_trips[activity.trip_id]:
            activities_by_trips[activity.trip_id][activity.destination_id] = []

        activities_by_trips[activity.trip_id][activity.destination_id].append(activity)

    return render_to_response(template_name, {
        'page_title': 'List of trips',
        'trips': trips,
        'activities_by_trips': activities_by_trips,
    })

模板


{% block content %}
    {% for trip in trips %}
        {{ trip.id }} - {{ trip.name }}

        {% for destination in trip.destinations.all %}
            {{ destination.city_name }}

            ** This is terrible code -- How to fix that **
            {% for key, value in activities_by_trips|dict_lookup:trip.id %}
                {% if value %}
                    {% for key_prime, value_prime in value|dict_lookup:destination.id %}
                       {{ value_prime.description }}
                    {% endfor %}
                {% endif %}
            {% endfor %}
        {% endfor %}
    {% endfor %}
{% endblock %}

简而言之,有人可以帮我总结一下所有的旅行和活动吗?实现这一目标的最佳方法是什么?模型是否正确?

谢谢!

【问题讨论】:

    标签: python django django-models django-templates django-views


    【解决方案1】:

    还有很大的改进空间。通过在 ManyToManyField 上使用 through,您可以显式定义连接表,我们可以方便地将其视为在特定旅行期间对城市的一次访问。在那次访问期间我们有活动,所以活动应该有一个访问的外键。

    对于表中的每个外键,Django 将为关系另一侧的对象集添加 API 便利管理器。 Destination 将有一个visit_set,但Trip 也会有。同样,由于Activity中的visit外键,每次访问都会有一个activity_set

    首先从模型开始:

    from django.db import models
    
    # Create your models here.
    class Destination(models.Model):
        city_name=models.CharField(max_length=50)
    
    class Trip(models.Model):
        departing_on=models.DateField()
        returning_on=models.DateField()
        destinations=models.ManyToManyField(Destination, through='Visit')
    
    class Visit(models.Model):
        destination=models.ForeignKey(Destination)
        trip=models.ForeignKey(Trip)
    
    class Activity(models.Model):
        name=models.CharField(max_length=50)
        visit=models.ForeignKey(Visit)
    

    然后让我们稍微更改一下list_trip,添加 print_trip 以清楚地了解模板中发生的情况:

    def list_trip(request, template_name = 'trip-list.html'):
        return render_to_response(template_name, {
            'page_title': 'List of trips',
            'trips': Trip.objects.all(),
            })
    
    def print_trips():
        for trip in Trip.objects.all():
            for visit in trip.visit_set.select_related().all():
                print trip.id, '-', visit.destination.city_name
                for act in visit.activity_set.all():
                    print act.name
    

    最后是改进的模板:

    {% block content %}
        {% for trip in trips %}
            {{ trip.id }} - {{ trip.name }}
    
            {% for visit in trip.visit_set.select_related.all %}
                {{ visit.destination.city_name }}
    
                {% for act in visit.activity_set.all %}
                     {{ act.name }}
                {% endfor %}
            {% endfor %}
        {% endfor %}
    {% endblock %}
    

    在性能方面还有更多改进空间。注意我使用了 select_related。这将在获取访问时预取所有目的地,因此 visit.destination.city_name 不会引起另一个数据库调用。然而,这不适用于反向多对多关系(在我们的例子中是所有 activity_set 的成员)。 Django 1.4 将推出名为 prefetch_related 的新方法,它也将解决这个问题。

    同时,请阅读Efficient reverse lookups,了解如何进一步减少 DB 命中数。在 cmets 中也很少提及现成的解决方案。

    【讨论】:

    • 谢谢digivampire!这正是我想要的。
    猜你喜欢
    • 2020-12-23
    • 1970-01-01
    • 2011-11-15
    • 2020-11-07
    • 2021-10-31
    • 2020-06-22
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    相关资源
    最近更新 更多