これらは不明確なテーブル名とフィールド名ですが、クエリは次のようになります。
(Restaurant.objects.filter(city=8,
cuisine__cuisinetype__cuisine="Italian").distinct().order_by('name')[:20])
ただし、そのデータベーススキーマに固定されていない限り、モデルは次のように表示されます。
class CuisineType(models.Model):
name = models.CharField(max_length=50)
class Meta:
db_table = 'cuisinetype'
class Restaurants(models.Model):
city = models.ForeignKey("City", null=True, blank=True) # Apparently defined elsewhere. Should be part of location?
name = models.CharField(max_length=50)
location = models.ForeignKey("Location", null=True, blank=True) # Apparently defined elsewhere.
cuisines = models.ManyToManyField(CuisineType)
その場合、クエリは次のようになります。
Restaurant.objects.filter(city=8, cuisines__name="Italian").order_by('name')[:20]
OK、コードに変更がないと仮定して、クエリを見ていきましょう。サブクエリから始めましょう。
SELECT DISTINCT res_id FROM cuisine
JOIN cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
WHERE cuisinetype.`cuisine` = 'Italian'
WHERE句を見ると、JOINが必要であることがわかります。結合を行うには、結合されたモデルの1つでリレーショナルフィールドを宣言する必要があります(Djangoは逆の関係を追加します。これに名前を付ける必要があります)。したがって、cuisine.cuisineid
を照合します `cuisinetype.cuisineidで。それは恐ろしい名前です。
これは多対多の関係であるため、ManyToManyField
が必要です。 。さて、Cuisine
を見てください モデル、それは本当にこのM2Mの結合テーブルです。 Djangoは、結合テーブルに2つのForeignKey
があることを想定しています。 フィールド、1つはジョイントの両側を指します。通常、正気を保つためにこれを作成します。どうやらあなたはそれほど幸運ではありません。したがって、手動で接続する必要があります。
「GID」フィールドはレコードの(役に立たない)IDフィールドのように見えるので、自動インクリメント整数であると仮定しましょう。 (確かに、CREATE TABLEコマンドを確認してください。)これで、Cuisine
を書き直すことができます。 正気に近づく何かにモデル化する:
class Cuisine(models.Model):
cuisinegid = models.AutoField(primary_key=True, db_column='CuisineGID')
cuisineid = models.ForeignKey("Cuisinetype", null=True,
db_column='CuisineID', blank=True)
res_id = models.ForeignKey("Restaurant", null=True, db_column='Res_ID',
blank=True)
class Meta:
db_table = 'cuisine'
モデルはまだ定義されていないため、モデル名は引用符で囲まれています(モデルはファイルの後半にあります)。これで、Djangoフィールド名が列名と一致する必要がなくなったので、より読みやすい名前に変更してみましょう。レコードIDフィールドは通常、単にid
という名前です。 、および外部キーは通常、関連するものにちなんで名付けられます:
class Cuisine(models.Model):
id = models.AutoField(primary_key=True, db_column='CuisineGID')
cuisine_type = models.ForeignKey("CuisineType", null=True,
db_column='CuisineID', blank=True)
restaurant = models.ForeignKey("Restaurant", null=True, db_column='Res_ID',
blank=True)
class Meta:
db_table = 'cuisine'
OK、ジョイントテーブルの定義は完了です。この間、同じものをCuisinetype
に適用してみましょう。 モデル。修正されたキャメルケースのクラス名に注意してください:
class CuisineType(models.Model):
id = models.AutoField(primary_key=True, db_column='CuisineID')
name = models.CharField(max_length=50, db_column='Cuisine', blank=True)
class Meta:
db_table = 'cuisinetype'
ついにRestaurant
にたどり着きました モデル。名前は単数;であることに注意してください オブジェクトは1つのレコードのみを表します。
dp_table
がないことに気づきました またはdb_column
だから、私は手足に出かけて、Djangoがそれを作成していると推測しています。つまり、id
を作成させることができます フィールドであり、コードから省略できます。 (そうでない場合は、他のモデルと同じように追加します。ただし、null許容レコードIDは使用しないでください。)そして、ここで料理のタイプがManyToManyField
になります。 生活:
class Restaurants(models.Model):
city_id = models.ForeignKey(null=True, blank=True)
name = models.CharField(max_length=50, blank=True)
location = models.ForeignKey(null=True, blank=True)
cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
null=True, blank=True)
M2Mフィールドの名前は複数形であることに注意してください。これは、その関係が複数のレコードにつながるためです。
このモデルに追加したいもう1つのことは、逆の関係の名前です。つまり、他のモデルからRestaurant
に戻る方法 。これを行うには、related_name
を追加します パラメーター。それらが同じであることは珍しいことではありません。
class Restaurant(models.Model):
city_id = models.ForeignKey(null=True, blank=True,
related_name="restaurants")
name = models.CharField(max_length=50, blank=True)
location = models.ForeignKey(null=True, blank=True,
related_name="restaurants")
cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
null=True, blank=True, related_name="restaurants")
これでようやく設定されました。それでは、クエリを見てみましょう:
SELECT restaurants.`name`, restaurants.`address`, cuisinetype.`cuisine`
FROM restaurants
JOIN cuisinetype ON cuisinetype.cuisineid = restaurants.`cuisine`
WHERE city_id = 8 AND restaurants.id IN (
SELECT DISTINCT res_id FROM cuisine
JOIN cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
WHERE cuisinetype.`cuisine` = 'Italian')
ORDER BY restaurants.`name`
LIMIT 20
これはFROM restaurants
なので 、そのモデルのデフォルトのオブジェクトマネージャーであるobjects
から始めます。 :
Restaurant.objects
WHERE
この場合の句はfilter()
です 呼び出すので、最初の用語に追加します:
Restaurant.objects.filter(city=8)
主キー値またはCity
を枯渇させることができます その用語の右側にあるオブジェクト。ただし、JOIN
が必要なため、クエリの残りの部分はより複雑になります。 。 Djangoでの結合は、リレーションフィールドを介した間接参照のように見えます。クエリでは、関連するフィールド名を二重アンダースコアで結合することを意味します:
Restaurant.objects.filter(city=8, cuisine_type__name="Italian")
Djangoは、Cuisine
で宣言されているため、どのフィールドに参加するかを知っています。 through=Cuisine
によって引き込まれるテーブル cuisine_types
のパラメータ 。また、M2M関係を経由しているため、サブクエリを実行することも認識しています。
つまり、SQLは次と同等になります:
SELECT restaurants.`name`, restaurants.`address`
FROM restaurants
WHERE city_id = 8 AND restaurants.id IN (
SELECT res_id FROM cuisine
JOIN cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
WHERE cuisinetype.`cuisine` = 'Italian')
途中です。ここで、SELECT DISTINCT
が必要です。 したがって、同じレコードの複数のコピーを取得することはありません:
Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
そして、表示する料理の種類を取り込む必要があります。結合テーブルにたどり着くだけで、関連するCuisineType
を取得するにはさらにクエリを実行する必要があるため、ここでのクエリは非効率的であることがわかります。 記録。推測:Djangoはあなたをカバーしています。
(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
.prefetch_related("cuisine_types"))
Djangoは2つのクエリを実行します。1つはジョイントIDを取得するためのクエリで、もう1つは関連するCuisineType
を取得するためのクエリです。 記録。その後、クエリ結果を介したアクセスはデータベースに戻る必要はありません。
最後の2つは順序です:
(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
.prefetch_related("cuisine_types").order_by("name"))
そしてLIMIT
:
(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
.prefetch_related("cuisine_types").order_by("name")[:20])
そして、Pythonの2行にパックされたクエリ(および関連するクエリ)があります。この時点では、クエリは実行されていません。何かをする前に、テンプレートのようなものに入れる必要があります:
def cuisinesearch(request, cuisine):
return render_to_response('cuisinesearch.html', {
'restaurants': (Restaurant.objects.filter(city=8,
cuisine_type__name="Italian").distinct()
.prefetch_related("cuisine_types").order_by("name")[:20])
})
テンプレート:
{% for restaurant in cuisinesearch %}
<h2>{{ restaurant.name }}</h2>
<div class="location">{{ restaurant.location }}</div>
<h3>Cuisines:</h3>
<ul class="cuisines">{% for ct in restaurant.cuisine_types.all %}
<li>{{ ct.name }}</li>{% endfor %}
</ul>
{% endfor %}