web-dev-qa-db-fra.com

Comment joindre deux tables sur un champ de clé étrangère en utilisant Django ORM?

Supposons que j'ai les modèles suivants:

class Position(models.Model):
    name = models.CharField()

class PositionStats(models.Model):
    position = models.ForeignKey(Position)
    averageYards = models.CharField()
    averageCatches = models.CharField()

class PlayerStats(models.Model):
    player = models.ForeignKey(Player)
    averageYards = models.CharField()
    averageCatches = models.CharField()

class Player(models.Model):
    name = models.CharField()
    position = models.ForeignKey(Position)

Je veux effectuer la requête SQL équivalente en utilisant l'ORM de Django:

SELECT *

FROM PlayerStats

JOIN Player ON player

JOIN PositionStats ON PositionStats.position = Player.position

Comment pourrais-je faire cela avec l'ORM de Django? La requête n'est pas exactement correcte, mais l'idée est que je veux une seule requête, utilisant l'ORM de Django, qui me donne PlayerStats jointe à PositionStats en fonction de la position du joueur.

21
Lee Schmidt

Ce n'est pas une requête, mais c'est assez efficace. Cela effectue une requête pour chaque table impliquée et les joint en Python. Plus sur prefetch_related ici: https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related

Player.objects.filter(name="Bob").prefetch_related(
        'position__positionstats_set', 'playerstats_set')
10
dokkaebi

Je travaille avec Django depuis un certain temps maintenant et j'ai eu du mal à comprendre les jointures de table, mais je pense que je comprends enfin et je voudrais transmettre cela aux autres afin qu'ils évitent la frustration que j'en ai ressentie.

Considérez le model.py suivant:

class EventsMeetinglocation(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=200)

    class Meta:
        managed = True
        db_table = 'events_meetinglocation'


class EventsBoardmeeting(models.Model):
    id = models.IntegerField(primary_key=True)
    date = models.DateTimeField()
    agenda_id = models.IntegerField(blank=True, null=True)
    location_id = models.ForeignKey(EventsMeetinglocation)
    minutes_id = models.IntegerField(blank=True, null=True)

    class Meta:
       managed = True
       db_table = 'events_boardmeeting'

Ici, nous pouvons voir que location_id dans EventsBoardmeeting est une clé étrangère pour l'id dans EventsMeetinglocation. Cela signifie que nous devrions pouvoir interroger les informations dans EventsMeetinglocation en passant par EventsBoardmeeting.

Considérez maintenant le views.py suivant:

def meetings(request):
    meetingData = EventsBoardmeeting.objects.all()
    return render(request, 'board/meetings.html', {'data': meetingData })

Comme indiqué plusieurs fois auparavant dans d'autres articles, Django s'occupe automatiquement des jointures. Lorsque nous interrogeons tout dans EventsBoardmeeting, nous obtenons également toutes les informations connexes par clé étrangère, mais la façon dont nous accédons ceci en html est un peu différent. Nous devons passer par la variable utilisée comme clé étrangère pour accéder aux informations associées à cette jointure. Par exemple:

{% for x in data %}
   {{ x.location_id.name }}
{% endfor %}

Ce qui précède fait référence à TOUS les noms de la table qui sont le résultat de la jointure sur la clé étrangère. x est essentiellement la table EventsBoardmeeting, donc lorsque nous accédons à x.location_id, nous accédons à la clé étrangère qui nous donne accès aux informations dans EventsMeetinglocation.

7
Connar Stone

select_related() et prefetch_related() est votre solution. Ils fonctionnent presque de la même manière mais ont une certaine différence.

select_related() fonctionne en créant une jointure SQL et en incluant les champs de l'objet associé dans l'instruction SELECT. Pour cette raison, select_related Obtient les objets associés dans la même requête de base de données. Mais cela ne fonctionne que pour une relation un-à-un ou un-à-plusieurs. L'exemple est ci-dessous-

entry = Entry.objects.select_related('blog').get(id=5)
or
entries = Entry.objects.filter(foo='bar').select_related('blog')

prefetch_related(), d'autre part, effectue une recherche distincte pour chaque relation et effectue la "jointure" en Python. Cela lui permet de pré-extraire des objets plusieurs-à-plusieurs et plusieurs-à-un, ce qui ne peut pas être fait en utilisant select_related. Ainsi prefetch_related Exécutera une seule requête pour chaque relation. Un exemple est donné ci-dessous-

Pizza.objects.all().prefetch_related('toppings')
4
Emdadul Sawon