ویدیو کار با متدهای select_related و prefetch_related در جنگو

امیرحسین بیگدلو June 2023

ابزار ORM جنگو به ما کمک می کند تا با استفاده از کد پایتون که بعداً به SQL ترجمه می شود، پرس و جوهایی را در پایگاه داده های رابطه ای انجام دهیم. ORM متدهایی را ارائه می دهد که برای بهینه سازی پرس و جوها در پایگاه داده استفاده می شود.متدهای select_related و prefetch_related جنگو میتوانند با استفاده از پایتون یا SQL جداول دیتابیس را به یکدیگر join بزنند. هدف بهینه سازی آنها کاهش تعداد پرس و جوها است.

 

تمام مثال‌های این مقاله از مدل زیر استفاده میکنند:

from django.db import models

class Author(models.Model):
   first_name = models.CharField(max_length=512)
   last_name = models.CharField(max_length=512)

class Book(models.Model):
   title = models.CharField(max_length=512)
   author = models.ForeignKey(Author, on_delete=models.CASCADE)

 

مدل Author با اطلاعات زیر پر شده:

id first_name last_name
1 William Shakespeare
2 Miguel de Cervantes

 

همچنین مدل Book با اطلاعات زیر پر شده:

id title author_id
1 Hamlet 1
2 Romeo and Juliet 1
3 Macbeth 1
4 Don Quixote 2

 

# تابع select_related جنگو

متد Select_related جنگو برای متصل کردن جداول با استفاده از INNER JOIN زبان SQL استفاده میشود. این متد یک QuerySet را برمی‌گرداند که روابط کلید خارجی را دنبال می‌کند و داده‌های شی مرتبط اضافی را هنگام اجرای پرس و جو انتخاب می‌کند. در این حالت دیگر نیازی نیست برای گرفتن اطلاعات مرتبط دوباره به دیتابیس کوئری بزنید. این توضیح ممکن است کمی انتزاعی به نظر برسد پس بهتر است یک مثال ببینیم.

 

بیایید بدون استفاده از select_related ابتدا به یک کوئری بزنیم:

>>> Book.objects.get(id=1).author.first_name
‘William’

 

دستور ORM بالا به کد sql زیر ترجمه خواهد شد:

SELECT "book"."id", "book"."title", "book"."author_id" FROM "book" WHERE "book"."id" = 1

SELECT "author"."id", "author"."first_name", "author"."last_name" FROM "author" WHERE "author"."id" = 1

 

همانطور که مشخص است برای گرفتن اطلاعات دوبار به دیتابیس کوئری زده شده. کوئری اول author_id مرتبط با کتاب را پیدا میکند و کوئری دوم اطلاعات مربوط به نویسنده را برمیگرداند. این روش اصلا بهینه نیست. به جای آن میتوانیم از متد select_related جنگو استفاده کنیم که هر دو جدول را در یک کوئری برمیگرداند:

>>> Book.objects.select_related('author').get(id=1).author.first_name
‘William’

 

این دستور به شکل زیر ترجمه خواهد شد:

SELECT "book"."id", "book"."title", "book"."author_id", "author"."id", "author"."first_name", "author"."last_name" FROM "book" INNER JOIN "author" ON ("book"."author_id" = "author"."id") WHERE "book"."id" = 1

 

ما هنوز هم نتیجه یکسانی دریافت کردیم اما این بار فقط با یک کوئری. میتوانیم ببینیم که select_related جنگو از INNER JOIN برای متصل کردن دو جدول به یکدیگر استفاده کرده است.

 

 

# تابع prefetch_related جنگو

متد prefetch_related جنگو هم دقیقا هدفی مشابه select_related دارد اما به روشی متفاوت این کار را انجام میدهد. متد prefetch_related هر دو جدول را پرس و جو می کند و آنها را با استفاده از Python (نه SQL) به هم متصل می کند، بنابراین اساساً کارایی آن تا حدودی کمتر از روش select_related است. اما prefetch_related ویژگی های دیگری نیز دارد.

 

بیایید ابتدا تفاوت‌های بین کوئری ساده و متد prefetch_related را در جنگو بررسی کنیم. همانطور که قبلاً دیدیم، کوئری ساده به دو کوئری SQL ترجمه می شود. آیا می توانیم با استفاده از متد prefetch_related نتیجه بهتری بگیریم؟

>>> Book.objects.prefetch_related('author').get(id=1).author.first_name
'William'

 

دستور زیر به شکل زیر ترجمه میشود:

SELECT "book"."id", "book"."title", "book"."author_id" FROM "book" WHERE "book"."id" = 1;

SELECT "author"."id", "author"."first_name", "author"."last_name" FROM "author" WHERE "author"."id" IN (1);

 

در حال حاضر نمیتوانیم هیچ نوع بهینه‌سازی ببینیم. پس بیایید یک مثال دیگر ببینیم:

books = Book.objects.prefetch_related('author')
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>]>

for book in books: 
    print(book.author.first_name)

# William
# William
# William
# Miguel

 

خروجی یکسان است اما دستور بالا به شکل زیر ترجمه میشود:

SELECT "book"."id", "book"."title", "book"."author_id" FROM "book";

SELECT "author"."id", "author"."first_name", "author"."last_name" FROM "author" WHERE "author"."id" IN (1, 2);

 

همانطور که می بینید، هیچ کوئری اضافی انجام نمی شود. در عوض، ما دو پرس و جو داریم که هر دو جدول را واکشی می کنند و پایتون این جداول را به هم وصل می کند. که البته مزایا و معایب خود را دارد. از نظر کارایی، select_related سریعتر و کارآمدتر است، اما select_related نمی تواند برای ManyToManyField استفاده شود. و این مزیت اصلی prefetch_related است.

 

روابط ManyToMany نباید با استفاده از SQL مدیریت شوند زیرا بسیاری از مشکلات عملکرد ممکن است هنگام برخورد با جداول بزرگ ظاهر شوند. به همین دلیل است که متد prefetch_related جداول را با پایتون به هم متصل میکند و از ایجاد اتصالات SQL بزرگ جلوگیری می‌کند.

ویدیوهای مشابه



آموزش duck typing در پایتون
امیرحسین بیگدلو

ارسال نظر


ali kazemi

1 ماه قبل پاسخ به نظر

سلام لطفا یه ویدئو تک قسمتی هم در مورد تغییرات جدید جنگو 5 ارائه بدید با تشکر

ارسال نظر



فردین ترکماند

3 ماه قبل پاسخ به نظر

سلام استاد
ویدیو عالی بود
فقط کاشکی برای نشون دادن query ها از پکیج django debug tolbar استفاده میکردین
و اگر امکانش هست نحوه استفاده از کلاس Prefetchهم یک ویدیو مجزا براش رکورد کنین!
با سپاس فراوان

ارسال نظر



باب

10 ماه قبل پاسخ به نظر

درود امیر جان
ممکنه خواهش کنم در مورد موارد Case Value When Func هم یه ویدیو درست کنی و مثال بزنی - توضیحات داکیومنت مفهوم نیست آنچنان
دستت درد نکنه

ارسال نظر



Reza

1 سال قبل پاسخ به نظر

اینو تو مصاحبه ازم پرسیدن منتها به صورت چالشی
چبزه جالب و مهمی بود از این ویدیو های نکته جنگویی ها زیاد بزارید

ارسال نظر



پارسا

1 سال قبل پاسخ به نظر

خیلی ممنون عالی و کاربردی

ارسال نظر



رضا

1 سال قبل پاسخ به نظر

خیلی ممنون عالی بود

میشه خواهش کنم یه ویدیو هم در مورد نحوه استفاده از transaction ها و همچنین نحوه استفاده select for update بسازید ؟
من هرچی داکومنت رو خوندم و تو اینترنت گشتم ، معنی دقیقشو رو نفهمیدم
تشکر

ارسال نظر



مونگارد