3

Je suis nouveau à Django et j'ai été impressionné jusqu'à présent par ses capacités. Je joue avec des modèles plus complexes et j'ai du mal à les utiliser correctement. En utilisant Django 1.3, j'essaye d'écrire une page de résumé qui présenterait les trois modèles ci-dessous avec la structure suivante. En d'autres termes, une liste de voyages avec leurs destinations et activités.Django et les modèles avec plusieurs clés étrangères

  • voyage 1
    • Destination 1
    • Destination 2
    • Activité 1
  • voyage 2
    • Destination 1
    • Activité 2

modèles

  • voyage < -> TripDestination < -> Destination (un voyage peut avoir plusieurs destinations)
  • Activité -> Voyage, activité -> Destination (une activité est défini pour un déplacement à un emplacement/une destination spécifique)
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)

J'essaye d'écrire une vue qui génèrerait une page avec la structure présentée ci-dessus. Le principal problème que j'ai en ce moment est d'afficher les activités pour un voyage et une destination spécifiques. Comme vous pouvez le voir dans le code ci-dessous, je construis un dictionnaire et je doute que ce soit la bonne chose à faire. De plus, la vue devient

Voir

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, 
    })

Modèle


{% 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 %} 

En bref, quelqu'un peut-il s'il vous plaît aidez-moi à obtenir un résumé de tous les voyages et activités? Quelle est la meilleure façon d'accomplir cela? Le modèle est-il correct?

Merci!

Répondre

13

Il y a beaucoup de place pour l'amélioration. En utilisant across sur ManyToManyField, vous pouvez définir explicitement la table de jointure, que nous pouvons facilement considérer comme une seule visite dans une ville au cours d'un voyage particulier. Au cours de cette visite, nous avons eu des activités, donc l'activité devrait avoir une clé étrangère à une visite.

Pour chaque clé étrangère dans une table, Django ajoutera un gestionnaire de commodité API pour les ensembles d'objets du côté opposé de la relation. Destination aura un visit_set, mais il en sera de même pour Trip. De même, en raison de visit foreignkey dans Activity chaque visite aura un activity_set.

début D'abord avec les modèles:

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) 

permet ensuite changer list_trip un peu, a ajouté print_trip pour la clarté de ce qui se passe dans le modèle:

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 

Et enfin le modèle amélioré:

{% 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 %} 

Il reste encore de la place pour l'amélioration t performance sage. Avis que j'ai utilisé select_related. Cela préextrapera toutes les destinations au moment où les visites sont récupérées, de sorte que visit.destination.city_name n'entraînera pas d'autre appel db. Cependant cela ne fonctionne pas pour inverser les relations ManyToMany (dans notre cas, tous les membres de activity_set). Django 1.4 sortira avec une nouvelle méthode appelée prefetch_related qui résoudra cela aussi. Pendant ce temps, lisez Efficient reverse lookups pour savoir comment réduire encore davantage le nombre de hits DB. Dans les commentaires, peu de solutions facilement disponibles sont également mentionnées.

+0

Merci digivampire! Ceci est exactement ce que je cherchais. – Martin

Questions connexes