3

Я новичок в Django, и до сих пор я был впечатлен его возможностями. Я играю с более сложными моделями, и у меня есть проблема, чтобы правильно их использовать. Используя Django 1.3, я пытаюсь написать сводную страницу, в которой представлены три модели ниже со следующей структурой. Другими словами, список поездок с местами назначения и мероприятиями.Django и модели с несколькими внешними ключами

  • Маршрутный 1
    • Направление 1
    • Направление 2
    • активность 1
  • поездки 2
    • Направление 1
    • активность 2

Модели

  • Trip < -> TripDestination < -> Destination (поездка может иметь несколько направлений)
  • активность -> Trip, активность -> Destination (деятельность является определенные для поездки в определенном месте/месте назначения)
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 %} 

Короче, может кто-то пожалуйста, помогите мне получить сводку всех поездок и мероприятий? Каков наилучший способ добиться этого? Является ли модель правильной?

Спасибо!

ответ

13

Существует множество возможностей для улучшения. Используя ManyToManyField, вы можете явно определить таблицу соединений, которую удобно рассматривать как одно посещение города во время конкретной поездки. Во время этого визита у нас были мероприятия, поэтому деятельность должна иметь визит в другую сторону.

Для каждого внешнего ключа в таблице Django добавит менеджер удобностей API для наборов объектов на противоположной стороне отношения. Destination будет иметь visit_set, но так будет Trip. Аналогично, из-за visit foreignkey в Activity каждый визит будет иметь 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 не будет выполнять другой вызов db. Однако это не работает для обратных отношений ManyToMany (в нашем случае все члены activity_set). Django 1.4 выйдет с новым методом, называемым prefetch_related, который также решит это.

В то же время ознакомьтесь с Efficient reverse lookups за идею о том, как еще больше уменьшить количество обращений к БД. В комментариях упоминаются также несколько доступных решений.

+0

Спасибо digivampire! Это именно то, что я искал. – Martin

Смежные вопросы