- åæ¸ã
- åèãªã³ã¯
- ç°å¢
- äºåæºå: ã¢ãã«ã®ä½æã¨ãã¼ã¿æå ¥
- No.0 çºè¡ãããSQLã確èªãã
- No.1 select_relatedã§è¦ªãåã
- No.2 select_relatedã§è¦ªã®è¦ªãåã
- No.3 prefetch_relatedã§è¤æ°ä»¶ã®å¤ãåã
- No.4 Prefetchãªãã¸ã§ã¯ãã§å¤ãfilter
- No.5 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ManyToMany-ManyToMany
- No.6 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ForeignKey-ManyToMany
- No.7 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ForeignKey-ManyToMany + select_related
- No.8 Prefetchã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³ãorder_byãã
- No.9 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ManyToMany-ForeignKey
- No.10 to_attrã§è¤æ°ã®çµãè¾¼ã¿ããã
- No.11 1ã¤å ã2ã¤å ã®ãªã¬ã¼ã·ã§ã³ãããããæ¡ä»¶ä»ãã§Prefetch(ManyToMany-ManyToMany)
- No.12 親ãã¼ã¹ã§1件ã®åãåå¾ãã
åæ¸ã
å»å¹´æ«ãDjangoãæ¸ãã¦ã¾ããã
Djangoã®ORMã¯ç°¡æ½ãªè¨è¿°ã§SQLã®çºè¡ã¨Pythonãªãã¸ã§ã¯ããæ©æ¸¡ããã¦ããã¦ä¾¿å©ã§ãã
ãããä½ãèããã«ä½¿ã£ã¦ããã¨SQLã®çºè¡æ°ãå¢ãã¦ãã¦ãããã©ã¼ãã³ã¹ãã©ãã©ãä¸ãã£ã¦ãã¾ãã å¹çã®ããSQLãçºè¡ãã¦ãããããã«select_relatedã¨prefetch_relatedã使ç¨ãã¾ãã
èªåã¯æ¯åããã®ãã¿ã¼ã³ã©ããããã ã£ã...ãã¨å¿ãã¦ãã¾ãã®ã§ããã¿ã¼ã³éãä½æãã¾ããã
以ä¸ã®ãããªäººåãã§ãã
- ã¢ãã«ãè¤éã«ãªã£ã¦ããã¨æ··ä¹±ãã¦ãã¯ã¨ãªåæ¸å®è£ ã®æãæ¢ã¾ã£ã¦ãã¾ãã
- select_related/prefetch_relatedã®åºæ¬çãªä½¿ç¨æ¹æ³ã¨ç®çã¯ç解ãã¦ããã
- SQLãæä½éç解ãã¦ãã(INNER JOIN, OUTER JOIN, WHERE, IN ãããã®åºæ¬æ§æ)
ãµã³ãã«ã¯å ¨ã¦django shellã§å®è¡ãã¦ããããã¼ã¿ä½æã³ã¼ããä¹ãã¦ããã®ã§ãããã«è©¦ããã¨ãã§ãã¾ãã
åèãªã³ã¯
æ¬è¨äºã¯å ¬å¼ããã¥ã¡ã³ãã®ä¾ãå ã«ãè£è¶³ã追å ããå 容ã§ãã
ç°å¢
ãã¼ã¸ã§ã³ | |
---|---|
MacOS Ventura | 13.5.2 |
Python3 | 3.11.4 |
Django | 5.0.1 |
Djangoã®ç°å¢ãæ§ç¯ããæ¹æ³ã¯ä»¥ä¸ãåèã«ãã¦ãã ããã
äºåæºå: ã¢ãã«ã®ä½æã¨ãã¼ã¿æå ¥
以ä¸ã®ããã¥ã¡ã³ãã§åºããã¦ããä¾ãå°ãæ¹å¤ãã¦ãã¾ãã
QuerySet API ãªãã¡ã¬ã³ã¹ | Django ããã¥ã¡ã³ã | Django
ãã¶ããããã³ã°ãã¬ã¹ãã©ã³ã®ã¢ãã«ã§ãã
- ãã¶ã¨ãããã³ã°ã¯å¤:å¤ã®é¢ä¿ã
- ã¬ã¹ãã©ã³ã¯pizzasãã£ã¼ã«ãã§ãã¶ã¨å¤:å¤ã®é¢ä¿ã«ããã¾ãã(æä¾ãããã¶å ¨ã¦)
- ã¬ã¹ãã©ã³ã¯best_pizzaãã£ã¼ã«ãã§ãã¶ã¨å¤:1ã®é¢ä¿ã«ããã¾ãã(ä¸çªäººæ°ã®ãã¶)
- ãã®æããã¶ã親ã§ã.
from django.db import models class Country(models.Model): name = models.CharField(max_length=256) def __str__(self): return self.name class Topping(models.Model): name = models.CharField(max_length=256) def __str__(self): return self.name class Pizza(models.Model): name = models.CharField(max_length=256) country = models.ForeignKey( Country, related_name='pizza', null=True, on_delete=models.CASCADE) toppings = models.ManyToManyField(Topping) def __str__(self): return self.name class Restaurant(models.Model): name = models.CharField(max_length=256) # 追å pizzas = models.ManyToManyField(Pizza, related_name='restaurants') best_pizza = models.ForeignKey( Pizza, related_name='championed_by', on_delete=models.CASCADE) def __str__(self): return self.name
ãã¤ã°ã¬ã¼ã·ã§ã³ãã¾ãã
(env) $ python manage.py makemigrations (env) $ python manage.py migrate
ãã¼ã¿æå ¥ãããã®ã§ãdjango shellã«å ¥ãã¾ãã
(env) $ python manage.py shell
ã·ã§ã«å é¨ã§ä»¥ä¸ã®ã³ã¼ããå®è¡ãã¾ãã
from app.models import Topping, Pizza, Restaurant, Country i = Country.objects.create(name='ã¤ã¿ãªã¢') Topping.objects.create(name='ããã') Topping.objects.create(name='ãã¯ã«ã¹') Topping.objects.create(name='ãã¼ã³ã³') Topping.objects.create(name='ãã¤ãããã«') Topping.objects.create(name='ãã¼ãº') Topping.objects.create(name='ç¼ãé') pizza_A = Pizza.objects.create(name='ãã¶A') pizza_A.toppings.set(Topping.objects.filter(name__in=['ããã', 'ãã¯ã«ã¹', 'ãã¼ã³ã³'])) pizza_A.country = i pizza_A.save() pizza_B = Pizza.objects.create(name='ãã¶B') pizza_B.toppings.set(Topping.objects.filter(name__in=['ããã', 'ãã¯ã«ã¹', 'ãã¤ãããã«', 'ãã¼ãº'])) pizza_C = Pizza.objects.create(name='ãã¶C') pizza_C.toppings.set(Topping.objects.filter(name__in=['ããã', 'ç¼ãé'])) restaurant_1 = Restaurant.objects.create(name='ã¬ã¹ãã©ã³1', best_pizza=pizza_A) restaurant_1.pizzas.set(Pizza.objects.filter(name__in=['ãã¶A', 'ãã¶B'])) restaurant_2 = Restaurant.objects.create(name='ã¬ã¹ãã©ã³2', best_pizza=pizza_C) restaurant_2.pizzas.set(Pizza.objects.filter(name__in=['ãã¶A', 'ãã¶C']))
No.0 çºè¡ãããSQLã確èªãã
é常SQLã®çºè¡ã¯django-debug-toolbarãå°å ¥ããããlogãã確èªãããã¨ãå¤ãã¨æãã¾ãã ä»åã¯django shellã ãã§å®çµãããã®ã§ãdjango.db.connection.queriesã確èªãã¾ãã ããã«çºè¡ãããSQLã®å±¥æ´ãå ¥ã£ã¦ãã¾ãã
django shellã¸ã®å ¥ãæ¹ãåæ²ãã¾ãã
(env) $ python manage.py shell
ã¾ããçºè¡ãããSQLé¨åã®ã¿ã確èªãããã®ã§ã以ä¸ã®ãã«ãã¼é¢æ°ãå®ç¾©ãã¦ããã¾ãã(django shellã«è²¼ãä»ããã°OKã§ã)
from django.db import reset_queries, connection def f(q): for qt in q: print(qt['sql']) # ã¯ã¨ãªç¢ºèª f(connection.queries) # ãªã»ãã reset_queries()
以éãå ¨ã¦django shellå é¨ã§å®è¡ãã¦ãã¾ãã
No.1 select_relatedã§è¦ªãåã
ã¬ã¹ãã©ã³: ä¸çªäººæ°ã®ãã¶ã¯å¤:1ã®é¢ä¿ã§ãã ã¬ã¹ãã©ã³(å)ããæ¤ç´¢ããã¨ãä¸çªäººæ°ã®ãã¶(親)ã¯1ã¤ã«ãã ã¾ãã¾ãã
ç´ ã®ç¶æ ã ã¨ã¬ã¹ãã©ã³ãåå¾ããã¯ã¨ãªã«å ããåå¾ãããã¬ã¹ãã©ã³ã®æ°ã ãä¸çªäººæ°ã®ãã¶ãåãã¯ã¨ãªãçºè¡ããã¦ãã¾ãã¾ãã
>>> for restaurant in Restaurant.objects.all(): print(f'{restaurant.name}åºã®ä¸çªäººæ°ã®ãã¶ã¯{restaurant.best_pizza.name}') ã¬ã¹ãã©ã³1åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶A ã¬ã¹ãã©ã³2åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶C
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT "app_pizza"."id", "app_pizza"."name" FROM "app_pizza" WHERE "app_pizza"."id" = 1 LIMIT 21 SELECT "app_pizza"."id", "app_pizza"."name" FROM "app_pizza" WHERE "app_pizza"."id" = 3 LIMIT 21
ãã®ãããªå ´åãSQLã§ã¯è¦ªãçµåãã¦åå¾ãã¾ãã Djangoã®ORMã§ã¯ãselect_relatedã«ãã£ã¦çµåãå¯è½ã§ãã
çºè¡ãããSQLã確èªããã¨ã確ãã«INNER JOINããã¦ãããã¯ã¨ãªçºè¡æ°ã¯1件ã¨ãªã£ã¦ãã¾ããããã¾ããã
>>> reset_queries() # åæåãã¦ããã¾ã. >>> for restaurant in Restaurant.objects.select_related('best_pizza').all(): print(f'{restaurant.name}åºã®ä¸çªäººæ°ã®ãã¶ã¯{restaurant.best_pizza.name}') ã¬ã¹ãã©ã³1åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶A ã¬ã¹ãã©ã³2åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶C
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_restaurant" INNER JOIN "app_pizza" ON ("app_restaurant"."best_pizza_id" = "app_pizza"."id")
No.2 select_relatedã§è¦ªã®è¦ªãåã
ããã«ã¢ã³ãã¼ã¹ã³ã¢ã«ãã£ã¦ã親ã®è¦ªã®...ã¨è¾¿ããã¨ãã§ãã¾ãã å¾åã¯ãLEFT OUTER JOINã¨ãªã£ã¦ãããã¨ã«æ³¨æãã¦ãã ããã(Countryã¯ãã¶Aã«ã ãè¨å®ãã¦ãã¾ãã)
for restaurant in Restaurant.objects.select_related('best_pizza__country').all(): print(f'{restaurant.name}åºã®ä¸çªäººæ°ã®ãã¶ã¯{restaurant.best_pizza.name}({restaurant.best_pizza.country})') ã¬ã¹ãã©ã³1åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶A(ã¤ã¿ãªã¢) ã¬ã¹ãã©ã³2åºã®ä¸çªäººæ°ã®ãã¶ã¯ãã¶C(None)
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id", "app_country"."id", "app_country"."name" FROM "app_restaurant" INNER JOIN "app_pizza" ON ("app_restaurant"."best_pizza_id" = "app_pizza"."id") LEFT OUTER JOIN "app_country" ON ("app_pizza"."country_id" = "app_country"."id")
ã¨ããã§ã以ä¸ã®ãããªã³ã¼ããæ¸ãå¿ è¦ã¯ããã¾ããã 親ã®è¦ªã®è¦ª...ã¨è¾¿ãæã¯ãä¸çªé ã親ãæå®ããã°è¯ãã§ãã
ããã«ã¢ã³ãã¼ã¹ã³ã¢ã§æå®ããã°ããã®éä¸ã®ãã¼ãã«ããã¡ãã¨çµåããã¾ãã
# åé·ãªä¾. best_pizzaã®æå®ã¯ä¸è¦. select_related('best_pizza', 'best_pizza__country')
No.3 prefetch_relatedã§è¤æ°ä»¶ã®å¤ãåã
ãã解説ããã¦ããåºæ¬ã®å½¢ã§ãã
ãã¶ã¨ãããã³ã°ã¯å¤:å¤ã®é¢ä¿ã§ãããã¶ããã¼ã¹ã«ãã¦åå¾ãã¾ãã
åå¾ããããããã®ãã¶ã®ããããã³ã°ãå ¨ã¦åå¾ããã ã¨ããã±ã¼ã¹ãèãã¾ãã 以ä¸ã®ããã«ã¢ã¯ã»ã¹ããã¨ãã¶ãåå¾ããã¯ã¨ãª(1ã¤ã)ã«å ããåå¾ãããã¶ã®æ°ã ãããã®ãã¶ã«ç´ã¤ããããã³ã°ãåå¾ããã¯ã¨ãªãçºè¡ããã¦ãã¾ãã¾ãã
f(connection.queries) reset_queries() for pizza in Pizza.objects.all(): print(f'{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ãã¶B ããã,ãã¯ã«ã¹,ãã¤ãããã«,ãã¼ãº ãã¶C ããã,ç¼ãé
>>> f(connection.queries) SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" = 1 SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" = 2 SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" = 3
ãã®ãããªã±ã¼ã¹ã§ã¯ãSQLã¨ããç°ãªãæ¹æ³ãåãã¾ãã
prefetch_relatedã«ãã£ã¦ãããã³ã°ããããããå¥ã®ã¯ã¨ãªã§åå¾ããPythonã³ã¼ãã«ãã£ã¦çµåãã¾ã.
prefetch_relatedã¯ãã£ãã·ã¥æ©è½(Pythonå´ã®æ©è½)ã ã¨æèããã¨ãç解ããããã¨èªåã¯æãã¾ãã
ã§ã¯å®éã«ã¯ã¨ãªãè¦ã¦ã¿ã¾ãã
>>> reset_queries() >>> for pizza in Pizza.objects.prefetch_related('toppings').all(): print(f'{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ãã¶B ããã,ãã¯ã«ã¹,ãã¤ãããã«,ãã¼ãº ãã¶C ããã,ç¼ãé
>>> f(connection.queries) SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 2, 3)
1ã¤ãã®ã¯ã¨ãªã§ãã¶(idã1,2,3)ãåå¾ããå¾ã«ã2ã¤ãã®ã¯ã¨ãªãçºè¡ããã¦ãã¾ãã
ãã¶ã®IDãINå¥ã§å ¨ã¦æå®ããå¿ è¦ã«ãªããããã³ã°ãå ¨é¨ãããããåå¾ããã¯ã¨ãªã§ãã ãã®ã¯ã¨ãªã®çµæããã£ãã·ã¥ãã¦ãããä¸è¨ã®printã§å¿ è¦ã«ãªã£ãæã«å©ç¨ãã¦ããã¤ã¡ã¼ã¸ã§ãã
表ããã¯è¦ãã¾ããããPythonã®ã³ã¼ãã«ãããã£ãã·ã¥ãã該å½é¨åãè¦ã¤ãã¦ãã¾ãã
(è£è¶³) ä¸è¨ã®èª¬æã®ã½ã¼ã¹ã¯ãã¡ãã QuerySet API ãªãã¡ã¬ã³ã¹ | Django ããã¥ã¡ã³ã | Django
No.4 Prefetchãªãã¸ã§ã¯ãã§å¤ãfilter
prefetch_relatedã¯ä¸è¨ã®ããã«ãã£ãã·ã¥ããä»çµã¿ã§ãã
ãªã®ã§ããã£ãã·ã¥ããã¯ã¨ãªã¨ã¯ç°ãªããã¿ã¼ã³ã§ã¢ã¯ã»ã¹ããã¨ããããprefetch_relatedã®åã ãã¯ã¨ãªãå¢ãã¦ç¡é§ã«ãªãã¾ã
以ä¸ã®ä¾ã¯ãã¢ã¯ã»ã¹æã«filterã使ã£ã¦ãã¾ãããã®å ´åãprefetchããçµæã¯å©ç¨ãããå度SQLãçºè¡ããã¾ãã
- prefetch_relatedã§ã¯allãæå®.
- ã¢ã¯ã»ã¹æã«ã¯filterãæå®.
for pizza in Pizza.objects.prefetch_related('toppings').all(): print(f'{pizza.name}', ','.join([t.name for t in pizza.toppings.filter(id__gte=3)])) ãã¶A ãã¼ã³ã³ ãã¶B ãã¤ãããã«,ãã¼ãº ãã¶C ç¼ãé
>>> f(connection.queries) SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 2, 3) SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE ("app_pizza_toppings"."pizza_id" = 1 AND "app_topping"."id" >= 3) SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE ("app_pizza_toppings"."pizza_id" = 2 AND "app_topping"."id" >= 3) SELECT "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE ("app_pizza_toppings"."pizza_id" = 3 AND "app_topping"."id" >= 3)
prefetchããåããã£ã«ã¿ã¼ããæã¯ãPrefetchãªãã¸ã§ã¯ãã§æå®ãã¾ãã
toppingså´ã¯ãåã«allãæå®ãã¦ãã¾ãããã¡ãã¨ãã£ã«ã¿ããã¦ãã¾ãã ãã®æ¸ãæ¹ã¯ãallã®çµæãä¸æ¸ããã¦ãã¾ããããªã¤ã¡ã¼ã¸ã§ãã
from django.db.models import Prefetch >>> reset_queries() >>> for pizza in Pizza.objects.prefetch_related(Prefetch('toppings', queryset=Topping.objects.filter(id__gte=3))): print(f'{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ãã¶A ãã¼ã³ã³ ãã¶B ãã¤ãããã«,ãã¼ãº ãã¶C ç¼ãé
>>> f(connection.queries) SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE ("app_topping"."id" >= 3 AND "app_pizza_toppings"."pizza_id" IN (1, 2, 3))
to_attrå±æ§ãæå®ãããã¨ã§ãPrefetchãªãã¸ã§ã¯ãã§ã«ã¹ã¿ã ãããã£ãã·ã¥ã«æ示çã«ã¢ã¯ã»ã¹ã§ããã®ã§ããã¡ãã®è¨è¿°æ¹æ³ãããããã§ãã
>>> for pizza in Pizza.objects.prefetch_related(Prefetch('toppings', queryset=Topping.objects.filter(id__gte=3), to_attr='filtered_toppings')): print(f'{pizza.name}', ','.join([t.name for t in pizza.filtered_toppings]))
No.5 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ManyToMany-ManyToMany
ããã«ã¢ã³ãã¼ã¹ã³ã¢ã§ç¹ããã¨ã§æå®ã§ãã¾ãã
- ã¬ã¹ãã©ã³ããã¼ã¹ã«ãæä¾ãããã¶ã¨ãã®ãããã³ã°ãå ¨ã¦åå¾ããã
- ã¬ã¹ãã©ã³âï¸ãã¶âï¸ãããã³ã°
- ã¬ã¹ãã©ã³ã«ç´ã¤ãå
¨ã¦ã®ãã¶ãåå¾ããã
- ãã¶ã«ç´ã¤ãå ¨ã¦ã®ãããã³ã°ãåå¾ããã
- ã¬ã¹ãã©ã³ã«ç´ã¤ãå
¨ã¦ã®ãã¶ãåå¾ããã
for restaurant in Restaurant.objects.prefetch_related('pizzas__toppings').all(): print(f'{restaurant.name}åºã®ãã¶ä¸è¦§') for pizza in restaurant.pizzas.all(): print(f'\t{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ã¬ã¹ãã©ã³1åºã®ãã¶ä¸è¦§ ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ãã¶B ããã,ãã¯ã«ã¹,ãã¤ãããã«,ãã¼ãº ã¬ã¹ãã©ã³2åºã®ãã¶ä¸è¦§ ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ãã¶C ããã,ç¼ãé
- prefetch_relatedãªããªãåã¬ã¹ãã©ã³ãåãã¶ãã¨ã«ã¯ã¨ãªãçºçãã¾ãã
- prefetch_relatedã§ä»¥ä¸ã®3ã¤ã®ã¯ã¨ãªã«ã¾ã¨ã¾ãã¾ãã
- ã¬ã¹ãã©ã³ä¸è¦§ã®åå¾
- ã¬ã¹ãã©ã³ã®IDãINå¥ã§æå®ãã対å¿ãããã¶ãå ¨ã¦åå¾ã
- ãã¶ã®IDãINå¥ã§æå®ãã対å¿ãããããã³ã°ãå ¨ã¦åå¾ã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT ("app_restaurant_pizzas"."restaurant_id") AS "_prefetch_related_val_restaurant_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" INNER JOIN "app_restaurant_pizzas" ON ("app_pizza"."id" = "app_restaurant_pizzas"."pizza_id") WHERE "app_restaurant_pizzas"."restaurant_id" IN (1, 2) SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 2, 3)
No.6 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ForeignKey-ManyToMany
â»æ¬¡ã®No.7ã®å£åçã§ã
No.5ã¨åãããããã«ã¢ã³ãã¼ã¹ã³ã¢ã§ç¹ããã¨ãã§ãã¾ããFKã§çµåããã¢ãã«ãéã«ãã£ã¦ãåé¡ããã¾ããã
- ã¬ã¹ãã©ã³ï¸âä¸çªäººæ°ã®ãã¶âï¸ãããã³ã°
- ã¬ã¹ãã©ã³ã«FKã§ç´ã¤ãä¸çªäººæ°ã®ãã¶
- ãã¶ã«ç´ã¤ãå ¨ã¦ã®ãããã³ã°åå¾
for restaurant in Restaurant.objects.prefetch_related('best_pizza__toppings').all(): print(f'{restaurant.name}åºã®ä¸çªäººæ°ã®ãã¶') print(f'\t{restaurant.best_pizza.name}', ','.join([t.name for t in restaurant.best_pizza.toppings.all()])) ã¬ã¹ãã©ã³1åºã®ä¸çªäººæ°ã®ã㶠ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ã¬ã¹ãã©ã³2åºã®ä¸çªäººæ°ã®ã㶠ãã¶C ããã,ç¼ãé
- prefetch_relatedãªããªãåãã¶ãã¨ã«ã¯ã¨ãªãçºçãã¾ãã
- prefetch_relatedã§3ã¤ã®ã¯ã¨ãªã«ã¾ã¨ãããã¨ãã§ãã¾ãã
- ã¬ã¹ãã©ã³ä¸è¦§ã®åå¾
- ã¬ã¹ãã©ã³ã®IDãINå¥ã§æå®ãã対å¿ããä¸çªäººæ°ã®ãã¶ãåå¾(FKãªã®ã§1件ã®ã¿)ã
- ãã¶ã®IDãINå¥ã§æå®ãã対å¿ãããããã³ã°ãå ¨ã¦åå¾ã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" WHERE "app_pizza"."id" IN (1, 3) SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 3)
No.7 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ForeignKey-ManyToMany + select_related
No.6ã®æ¹åçã§ããForeignKeyã®é¨åã¯ãprefetchããselect_relatedã使ã£ã¦SQLã¬ãã«ã§å¹çåããæ¹ãåªãã¦ãã¾ãã
主ã¯ã¨ãª(select_relatedã®"å¾"ã«äºåèªã¿è¾¼ã¿(prefetch)ãèµ°ãã®ãã¤ã¡ã¼ã¸ããã¨åãããããã§ãã
for restaurant in Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings').all(): print(f'{restaurant.name}åºã®ä¸çªäººæ°ã®ãã¶') print(f'\t{restaurant.best_pizza.name}', ','.join([t.name for t in restaurant.best_pizza.toppings.all()])) ã¬ã¹ãã©ã³1åºã®ä¸çªäººæ°ã®ã㶠ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ã¬ã¹ãã©ã³2åºã®ä¸çªäººæ°ã®ã㶠ãã¶C ããã,ç¼ãé
- 2ã¤ã®ã¯ã¨ãªã«ã¾ã¨ãããã¨ãã§ãã¾ãã
- ã¬ã¹ãã©ã³ä¸è¦§ + ä¸çªäººæ°ã®ãã¶(FK)ãåæã«åå¾
- ãã¶ã®IDãINå¥ã§æå®ãã対å¿ãããããã³ã°ãå ¨ã¦åå¾ã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_restaurant" INNER JOIN "app_pizza" ON ("app_restaurant"."best_pizza_id" = "app_pizza"."id") SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 3)
No.8 Prefetchã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³ãorder_byãã
- ã¬ã¹ãã©ã³âï¸ãã¶âï¸ãããã³ã°: ãããã³ã°ãnameã®éé ã«ãã
- ä¸éã«ãããã¶ãprefetchããã¦ããç¹ã«æ³¨æ(No.5ããã¼ã¹ã«èãã¾ã)
>>> for restaurant in Restaurant.objects.prefetch_related(Prefetch('pizzas__toppings', queryset=Topping.objects.order_by('-name'))): print(f'{restaurant.name}åºã®ãã¶ä¸è¦§') for pizza in restaurant.pizzas.all(): print(f'\t{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ã¬ã¹ãã©ã³1åºã®ãã¶ä¸è¦§ ãã¶A ãã¼ã³ã³,ãã¯ã«ã¹,ããã ãã¶B ãã¯ã«ã¹,ãã¤ãããã«,ããã,ãã¼ãº ã¬ã¹ãã©ã³2åºã®ãã¶ä¸è¦§ ãã¶A ãã¼ã³ã³,ãã¯ã«ã¹,ããã ãã¶C ç¼ãé,ããã
- ãã¶IDãINã«æå®ããããããã³ã°åå¾ã¯ã¨ãªã«ORDER_BYãã¤ãã¾ãã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT ("app_restaurant_pizzas"."restaurant_id") AS "_prefetch_related_val_restaurant_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" INNER JOIN "app_restaurant_pizzas" ON ("app_pizza"."id" = "app_restaurant_pizzas"."pizza_id") WHERE "app_restaurant_pizzas"."restaurant_id" IN (1, 2) SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE "app_pizza_toppings"."pizza_id" IN (1, 2, 3) ORDER BY "app_topping"."name" DESC
No.9 prefetch_relatedã§2ã¤å ã®ãªã¬ã¼ã·ã§ã³: ManyToMany-ForeignKey
No.7ã¯ãã¯ã¨ãªå¯¾è±¡ã®ç´æ¥ã®é¢é£å ãForeignKeyããã®å ãManyToManyã§ããã ä»åã¯ç´æ¥ã®é¢é£å ãManyToManyã§ããã®å ã«ForeignKeyã§çµåããã¢ãã«ãããå ´åã§ãã
Pretetchã§åå¾ããã¨ãã対象ã®FKãJOINãããæ示 ã¨ããã¤ã¡ã¼ã¸ãåãããããã¨æãã¾ãã Prefetchã®querysetã§select_relatedã使ãã®ããã¤ã³ãã§ãã
- ãã¶âï¸ã¬ã¹ãã©ã³âä¸çªäººæ°ã®ãã¶
- ãã¶âï¸ã¬ã¹ãã©ã³: prefetchã§å¥ã¯ã¨ãªã«ãã¾ã(ãã¶IDãINå¥æå®)
- ã¬ã¹ãã©ã³âä¸çªäººæ°ã®ãã¶: select_relatedã§SQLã§çµåããç¶æ ã§åå¾ãã¾ãã
for pizza in Pizza.objects.prefetch_related(Prefetch('restaurants', queryset=Restaurant.objects.select_related('best_pizza'))): print(f'{pizza.name}ãæä¾ããã¦ãã¬ã¹ãã©ã³ä¸è¦§') for restaurant in pizza.restaurants.all(): print(f'\t{restaurant}ã®ä¸çªäººæ°ã®ãã¶ã¯: {restaurant.best_pizza.name}') ãã¶Aãæä¾ããã¦ãã¬ã¹ãã©ã³ä¸è¦§ ã¬ã¹ãã©ã³1ã®ä¸çªäººæ°ã®ãã¶ã¯: ãã¶A ã¬ã¹ãã©ã³2ã®ä¸çªäººæ°ã®ãã¶ã¯: ãã¶C ãã¶Bãæä¾ããã¦ãã¬ã¹ãã©ã³ä¸è¦§ ã¬ã¹ãã©ã³1ã®ä¸çªäººæ°ã®ãã¶ã¯: ãã¶A ãã¶Cãæä¾ããã¦ãã¬ã¹ãã©ã³ä¸è¦§ ã¬ã¹ãã©ã³2ã®ä¸çªäººæ°ã®ãã¶ã¯: ãã¶C
æåã«åå¾ãããã¶IDãINå¥ã«ãã¦ã¾ã¨ãã¦ã¬ã¹ãã©ã³ãä¸çºã§åãã¦ãããã¨ã«æ³¨ç®ãã¦ãã ããã ããã«ãããããã®ã¬ã¹ãã©ã³ã®æè¯ãã¶ã¯SQLã®æç¹ã§çµåã§ãã¦ãã¾ãã
>>> f(connection.queries) SELECT "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" SELECT ("app_restaurant_pizzas"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id", T4."id", T4."name", T4."country_id" FROM "app_restaurant" INNER JOIN "app_restaurant_pizzas" ON ("app_restaurant"."id" = "app_restaurant_pizzas"."restaurant_id") INNER JOIN "app_pizza" T4 ON ("app_restaurant"."best_pizza_id" = T4."id") WHERE "app_restaurant_pizzas"."pizza_id" IN (1, 2, 3)
çç¥ãã¾ãããprefetchãªã©ãã¤ããªãç¶æ ã ã¨ä»¥ä¸ã®ããã«ã¯ã¨ãªãããããçºè¡ããã¾ãã - ãã¶ä¸è¦§ãåå¾ããããããã®ãã¶IDã«å¯¾ãã¦ã¬ã¹ãã©ã³ã1件ãã¤ã¯ã¨ãªã§åå¾ã - ãã®ã¬ã¹ãã©ã³ã®æè¯ãã¶IDãã¯ã¨ãªã«ãã¦ãå度ãã¶ãåå¾ããã
No.10 to_attrã§è¤æ°ã®çµãè¾¼ã¿ããã
- åã対象ãè¤æ°ã®ãã¿ã¼ã³ã§åæã«çµã£ã¦ä½¿ãããã±ã¼ã¹ã
- ã¬ã¹ãã©ã³âï¸ã㶠ã§ããã¶ãè¤æ°ã®æ¹æ³ã§çµã
- ããã¬ã¹ãã©ã³ã«ã²ãã¤ããã¤ã¿ãªã¢ã®ãã¶ä¸è¦§ ã¨ å ¨ã¦ã®ãã¶ä¸è¦§ãåæã«åå¾ããã
ä½è«ã§ãããitaly_pizzaã¨all_pizzaãå®ç¾©ããæç¹ã§ã¯SQLã¯çºè¡ããã¦ããªãã¨ããç¹ã大äºã§ãã QuerySetã¯é 延è©ä¾¡ãªã®ã§ãå®éã®å¤ãåå¾ããã¾ã§çºè¡ããã¾ããã
italy_pizza = Pizza.objects.filter(country=italy) all_pizza = Pizza.objects.all() for restaurant in Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=italy_pizza, to_attr='italy'), Prefetch('pizzas', queryset=all_pizza, to_attr='all_pizzas')): print(f'{restaurant.name}åº') print('\t', ','.join([pizza.name for pizza in restaurant.italy])) print('\t', ','.join([pizza.name for pizza in restaurant.all_pizzas])) ã¬ã¹ãã©ã³1åº ãã¶A ãã¶A,ãã¶B ã¬ã¹ãã©ã³2åº ãã¶A ãã¶A,ãã¶C
Prefetchãæå®ããæ°ã ããSQLãå¢ãã¾ãã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT ("app_restaurant_pizzas"."restaurant_id") AS "_prefetch_related_val_restaurant_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" INNER JOIN "app_restaurant_pizzas" ON ("app_pizza"."id" = "app_restaurant_pizzas"."pizza_id") WHERE ("app_pizza"."country_id" = 1 AND "app_restaurant_pizzas"."restaurant_id" IN (1, 2)) SELECT ("app_restaurant_pizzas"."restaurant_id") AS "_prefetch_related_val_restaurant_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" INNER JOIN "app_restaurant_pizzas" ON ("app_pizza"."id" = "app_restaurant_pizzas"."pizza_id") WHERE "app_restaurant_pizzas"."restaurant_id" IN (1, 2)
No.11 1ã¤å ã2ã¤å ã®ãªã¬ã¼ã·ã§ã³ãããããæ¡ä»¶ä»ãã§Prefetch(ManyToMany-ManyToMany)
- 1ã¤å ã®ManyToManyãfilteræ¡ä»¶ä»ãã§prefetchãã2ã¤å ãfilterãã¦åå¾ãããã¿ã¼ã³.
- to_attrã§åä»ãããã¨ã§ã2ã¤å ãããã«ã¢ã³ãã¼ã¹ã³ã¢ã§æå®å¯è½ã«ãã¾ãã
- 以ä¸ããã¾ã§è¨è¿°ããé¡ä¼¼ãã¿ã¼ã³
- No.5: all(1ã¤å )ãall(2ã¤å )ã ã£ãã
- No.8: all(1ã¤å )ã2ã¤å ãorder_by
- No.9: all(1ã¤å )ã2ã¤å ã¯select_related
2ã¤å (ãããã³ã°)ãåå¾ããæã«ããã§ã«ãã£ã«ã¿ããã1ã¤å (ãã¶)ã«é¢é£ãããã®ã ãåå¾ããããã¨ããã®ããã¤ã³ãã§ãã
italy_pizza = Pizza.objects.filter(country=italy) for restaurant in Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=italy_pizza, to_attr='italy'), Prefetch('italy__toppings', queryset=Topping.objects.filter(id__lte=2), to_attr='topping_2')): print(f'{restaurant.name}åº') for pizza in restaurant.italy: print(f'\t{pizza.name}', ','.join([t.name for t in pizza.topping_2])) ã¬ã¹ãã©ã³1åº ãã¶A ããã,ãã¯ã«ã¹ ã¬ã¹ãã©ã³2åº ãã¶A ããã,ãã¯ã«ã¹
ä¸ã¤å (Pizza)ãã¬ã¹ãã©ã³ã®åå¾çµæã¨country=1ã§ãã£ã«ã¿ããã¦ããã ãã®çµæã®ãã¶IDã3ã¤ãã®toppingåå¾æã«INå¥ã§ä½¿ããid<=2ã®ãã£ã«ã¿ãåæã«å®è¡ãã¦ããã
>>> f(connection.queries) SELECT "app_restaurant"."id", "app_restaurant"."name", "app_restaurant"."best_pizza_id" FROM "app_restaurant" SELECT ("app_restaurant_pizzas"."restaurant_id") AS "_prefetch_related_val_restaurant_id", "app_pizza"."id", "app_pizza"."name", "app_pizza"."country_id" FROM "app_pizza" INNER JOIN "app_restaurant_pizzas" ON ("app_pizza"."id" = "app_restaurant_pizzas"."pizza_id") WHERE ("app_pizza"."country_id" = 1 AND "app_restaurant_pizzas"."restaurant_id" IN (1, 2)) SELECT ("app_pizza_toppings"."pizza_id") AS "_prefetch_related_val_pizza_id", "app_topping"."id", "app_topping"."name" FROM "app_topping" INNER JOIN "app_pizza_toppings" ON ("app_topping"."id" = "app_pizza_toppings"."topping_id") WHERE ("app_topping"."id" <= 2 AND "app_pizza_toppings"."pizza_id" IN (1))
2ã¤å ããã£ã«ã¿ããªããªã以ä¸ã®ããã«æ¸ãã°è¯ãã§ãã
>>> for restaurant in Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=italy_pizza, to_attr='italy'), 'italy__toppings'): print(f'{restaurant.name}åº') for pizza in restaurant.italy: print(f'\t{pizza.name}', ','.join([t.name for t in pizza.toppings.all()])) ã¬ã¹ãã©ã³1åº ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³ ã¬ã¹ãã©ã³2åº ãã¶A ããã,ãã¯ã«ã¹,ãã¼ã³ã³
No.12 親ãã¼ã¹ã§1件ã®åãåå¾ãã
- åãã親ã¢ãã«.get(æ¤ç´¢æ¡ä»¶)ãããããªã±ã¼ã¹
- åå¾çµæã¯1件ã§ãListå½¢å¼ã«ãªããããã¯ã©ããããããªãã£ã½ãã
- prefetchã§æå®ãã0çªç®ãåå¾ããã
åå´ããæ¤ç´¢ãã¦ãé¢é£ãã親ãselect_relatedãããã¨ãã§ãããªããã¡ããæ¡ç¨ãã.