web-dev-qa-db-fra.com

Comment accéder aux propriétés d'une table plusieurs-à-plusieurs "à travers" à partir d'un modèle Django?

Depuis la documentation Django ...

Lorsque vous ne traitez que de simples relations plusieurs-à-plusieurs telles que le mélange et la correspondance de pizzas et de garnitures, un ManyToManyField standard est tout ce dont vous avez besoin. Cependant, vous devrez parfois associer des données à la relation entre deux modèles.

Par exemple, considérons le cas d'une application qui suit les groupes musicaux auxquels appartiennent les musiciens. Il existe une relation plusieurs-à-plusieurs entre une personne et les groupes dont elle est membre, vous pouvez donc utiliser un ManyToManyField pour représenter cette relation. Cependant, il y a beaucoup de détails sur l'adhésion que vous voudrez peut-être collecter, comme la date à laquelle la personne a rejoint le groupe.

Pour ces situations, Django vous permet de spécifier le modèle qui sera utilisé pour régir la relation plusieurs-à-plusieurs. Vous pouvez ensuite mettre des champs supplémentaires sur le modèle intermédiaire. Le modèle intermédiaire est associé avec le ManyToManyField utilisant l'argument through pour pointer vers le modèle qui servira d'intermédiaire. Pour notre exemple de musicien, le code ressemblerait à ceci:

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __unicode__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Maintenant que vous avez configuré votre ManyToManyField pour utiliser votre modèle intermédiaire (adhésion, dans ce cas), vous êtes prêt à commencer à créer des relations plusieurs-à-plusieurs. Pour ce faire, créez des instances du modèle intermédiaire:

ringo = Person.objects.create(name="Ringo Starr")
paul = Person.objects.create(name="Paul McCartney")
beatles = Group.objects.create(name="The Beatles")

m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason= "Needed a new drummer.")

m1.save()

beatles.members.all()
[<Person: Ringo Starr>]

ringo.group_set.all()
[<Group: The Beatles>]

m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason= "Wanted to form a band.")

beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

source: http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

Ma question est de savoir comment configurer ma vue et mon modèle pour accéder à ces attributs supplémentaires. Supposons que j'ai une page de groupe et que je souhaite afficher le nom du groupe, parcourir les enregistrements d'appartenance et afficher les noms et date_joined.

Dois-je passer un objet de bande au modèle? Ou dois-je transmettre les objets d'appartenance d'une manière ou d'une autre?

Et comment créer les boucles for dans le modèle?

Merci.

45
Alex

Le moyen le plus simple consiste simplement à passer la bande au modèle. Les modèles sont capables de parcourir les relations entre les modèles et il y a à la fois des membres et des gestionnaires de jeu de requêtes Membership_set sur Group. Voici donc comment je le ferais:

vue:

def group_details(request, group_id):
    group = get_object_or_404(Group, pk=group_id)
    return render_to_response('group_details.html',
                              {'group': group})

modèle:

<h2>{{ group.name }}</h2>
{% for membership in group.membership_set.all %}
    <h3>{{ membership.person }}</h3>
    {{ membership.date_joined }}
{% endfor %}
37
Rory Hart

Je ne sais pas si c'est seulement une solution ou non, mais passer des objets relation au modèle fonctionne certainement. À votre avis, obtenez les objets QuerySet of Membership:

rel = Membership.objects.filter( group = your_group ).select_related()

et passez-le au modèle, où vous pouvez le parcourir avec {% for %}

{% for r in rel %}
     {{ r.person.name }} joined group {{ r.group.name }} on {{ r.date_joined }}<br />
{% endfor %}

Notez que cela ne doit pas effectuer de requêtes supplémentaires en raison de select_related().

6
cji