web-dev-qa-db-ja.com

Django ORMを使用して、外部キーフィールドで2つのテーブルを結合するにはどうすればよいですか?

私は次のモデルを持っていると仮定しましょう:

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)

DjangoのORMを使用して同等のSQLクエリを実行したい:

SELECT *

FROM PlayerStats

JOIN Player ON player

JOIN PositionStats ON PositionStats.position = Player.position

DjangoのORMでこれを行うにはどうすればよいですか?クエリは正確ではありませんが、アイデアは、DjangoのORMを使用して、プレーヤーの位置に基づいてPlayerStatsと結合されたPositionStatsを提供する単一のクエリが欲しいということです。

21
Lee Schmidt

1つのクエリではありませんが、非常に効率的です。これは、関係する各テーブルに対して1つのクエリを実行し、それらをPythonで結合します。 prefetch_relatedの詳細: 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

私はDjangoでしばらく働いていて、テーブルの結合を理解するのにかなり苦労しましたが、私は最終的に理解し、他の人に伝えたいと思います彼らは私がそれで持っていた欲求不満を避けるかもしれません。

次のmodel.pyを検討してください。

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'

ここで、EventsBoardmeetingのlocation_idはEventsMeetinglocationのidの外部キーであることがわかります。これは、EventsBoardmeetingを経由してEventsMeetinglocationの情報を照会できることを意味します。

ここで、次のviews.pyを検討してください。

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

他の投稿で何度も述べたように、Djangoは自動的に結合を処理します。EventsBoardmeetingですべてを照会すると、外部キーでも関連情報を取得しますが、アクセス方法はこれはhtmlの場合とは少し異なり、その結合に関連する情報にアクセスするには、外部キーとして使用される変数を調べる必要があります。

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

上記は、外部キーの結合の結果であるテーブル内のすべての名前を参照しています。 xは本質的にEventsBoardmeetingテーブルであるため、x.location_idにアクセスするとき、EventsMeetinglocationの情報にアクセスできる外部キーにアクセスします。

7
Connar Stone

select_related()およびprefetch_related()がソリューションです。ほぼ同じように機能しますが、いくつかの違いがあります。

select_related()は、SQL結合を作成し、関連するオブジェクトのフィールドをSELECTステートメントに含めることで機能します。このため、_select_related_は同じデータベースクエリで関連オブジェクトを取得します。ただし、1対1または1対多の関係でのみ機能します。例は以下です-

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

一方、prefetch_related()は、各関係に対して個別のルックアップを実行し、Pythonで「結合」を実行します。これにより、多対多および多対1オブジェクトをプリフェッチできます。これは_select_related_を使用して実行することはできません。したがって、_prefetch_related_は各リレーションに対して1つのクエリのみを実行します。以下に例を示します

_Pizza.objects.all().prefetch_related('toppings')
_
4
Emdadul Sawon