MySQLでデータ取得する時はこうSQLを書くけど、DjangoのQuerySetを取得する時はどうするんだろうと思うことがあり、いろいろ試してみました。
環境
- Django 2.2.3
- Python 3.7.4
内部結合したテーブルのpk以外で検索したい
modelsはそれぞれコードと名前のフィールドを持ち、市テーブルに都道府県テーブルと紐づけるForeignKeyを設定しています。
紐づけるフィールドはpk(プライマリーキー)にするので、to_field等は指定していません。
models.py
from django.db import models
class Pref(models.Model):
pref_cd = models.IntegerField(null=False, unique=True)
pref_name = models.CharField(max_length=255, null=False)
class Meta:
db_table = 'pref'
class City(models.Model):
pref = models.ForeignKey(
Pref,
on_delete=models.SET_NULL,
null=True
)
city_cd = models.IntegerField(null=False)
city_name = models.TextField(max_length=255, null=False)
class Meta:
db_table = 'city'
unique_together = (('pref', 'city_cd'))
SQL
SELECT *
FROM city
INNER JOIN pref ON pref.id = city.pref_id
WHERE pref.pref_name = '{都道府県名}';
QuerySet
<ForeignKeyを設定したフィールド名>__<検索したいフィールド名>
で検索できます。
City.objects.filter(pref__pref_name='{都道府県名}')
内部結合したテーブルのpk以外でソートしたい
SQL
SELECT *
FROM city
INNER JOIN pref ON pref.id = city.pref_id
ORDER BY pref.pref_cd DESC;
QuerySet
検索と同じように<ForeignKeyを設定したフィールド名>__<ソートしたいフィールド名>
でソートできます。
City.objects.order_by('pref__pref_cd').reverse().all()
ForeignKeyを設定していないテーブル同士で結合して検索したい
ForeignKeyは設定せず、市テーブルにも都道府県名を持つようにしています。
models.py
from django.db import models
class Pref(models.Model):
pref_cd = models.IntegerField(null=False, unique=True)
pref_name = models.CharField(max_length=255, null=False)
class Meta:
db_table = 'pref'
class City(models.Model):
city_cd = models.IntegerField(null=False)
city_name = models.TextField(max_length=255, null=False)
pref_name = models.TextField(max_length=255, null=False)
class Meta:
db_table = 'city'
SQL
SELECT city_name
FROM city
INNER JOIN pref ON pref.pref_name = city.pref_name
WHERE pref.pref_cd = {都道府県コード};
QuerySet
City.objects.extra(
tables=['pref'],
where=['city.pref_name=pref.pref_name']
).extra(where=['pref.pref_cd={都道府県コード}']).all()
このクエリを出力してみるとわかりますが、
SQL的にはWHERE ((city.pref_name=pref.pref_name) AND (pref.pref_cd={都道府県コード}))
と結合はしていません。
ほしいデータが取得できたからよしとしましたが、prefテーブルのデータは取得できないので注意です。