ترتیب اجرای متدهای فیلدها توی django.forms

در مورد صحتسنجی فیلدهایی که هنگام اجرای یک form توی جنگو طی میشه ترتیب زیر طی میشه که هر کدوم از اونها رو میشه overwrite کرد که رفتار form وقتی که دیتا میگیره تغییر بدیم

این توابع رو توی کلاس Field از django.forms میتونید ببینید. لینک زیر بدرد بخوره:

https://docs.djangoproject.com/en/4.2/ref/forms/validation/

F() expression توی جنگو

تفاوت دو کد زیر چیست؟

استفاده از F() expression توی کد جنگوی بالا این امکان رو میده که تغییر فیلد و increment کردنش سمت دیتابیس آماده انجام بشه بجای اینکه پایتون یک واحد بهش اضافه کنه. یعنی یک query میره سمت دیتابیس و مقدار فیلد هرچی هست همونجا یه واحد اضافه میشه. اینجوری دیگه لازم نیست یک بار از دیتابیس بگیره و اضافه کنه و آپدیت شده رو بفرسته سمت دیتابیس و دو query اجرا کنه. اما برای اینکه مقدار آپدیت شده سمت دیتابیس را داشته باشیم:

چالش ORM جنگو – spanning multivalued relationship

در رابطه های ManyToMany یا OneToMany استفاده کردن از spanning داخل filter به model دیگر این سوال رو ایجاد میکنه که هر دو شرط باید روی یک آبجکت اجرا شه یا هر کدام از شرط ها جدا؟ کد زیر توی مستندات جنگو روشن این موضوع رو نشون داده:

تکه کد ORM جنگو – Queryset slicing – سوال مصاحبه

آیا ORM جنگو با اجرای کد زیر به دیتابیس query میزند؟ اگر آره چند Hit دیتابیس داریم؟

ORM جنگو lazy است یعنی در هر حالتی به دیتابیس دسترسی نمیگیرد مگر اینکه بخواهد queryset را کامل از دیتابیس بگیرد. درحالتی که از slicing پایتون برای یک queryset استفاده می کنیم ORM یک queryset جدید برمیگردونه و اون رو اجرا نمیکنه. یعنی توی خط اول به دیتابیس Hit نداریم. قاعدتا باید توی خط دوم هم همین باشه اما slicing پایتون با step استثنا هست و توی خط دوم یه Hit به دیتابیس داریم.

تکه کد – سوال مصاحبه

کدهای زیر با هم چه تفاوتی دارند؟

در شرط اول QuerySet اجرا نشده و فقط یک دستور از دیتابیس گرفته شده که آیا این queryset عضوی دارد یا خیر. جنگو این بررسی رو در بهینه ترین و کوتاه ترین حالت انجام میده و از حالت دوم که queryset رو اجرا کرده کمی سریع تره. نکته اینه که در حالتی که میدونیم کهم در هر صورت در ادامه برنامه قراره my_queryset رو اجرا کنیم (چه عضو داشته باشه چه نه) بهتره که از حالت دوم استفاده کنیم که دیگه یک بار اضافه از exists استفاده نکرده باشیم. چون همینطور که میدونیم اگر my_queryset رو از دیتابیس بیگیریم دفعات بعدی دیگه از دیتابیس نمیگیریم و cache میشه.

همین داستان رو برای تابع contains() هم داریم.

مشکل N+1 – سوال مصاحبه (دوم)

کد زیر چند Hit دیتابیس دارد؟

همچنان N+2!!!

حتی با وجود اینکه از prefetch_related استفاده شده است دوباره در داخل حلقه به N دسترسی مجدد به دیتابیس وجود دارد. چرا که prefetch_related بالا برای Pizaa.objects.all بوده ولی پایین از filter استفاده کردیم که ORM جنگو دسترسی بالایی را در نظر نمیگیرد و دوباره query میزند. اتفاقا بدتره چرا که یه query بالا هم اضافی هست. راه حل زیر درست میشه:

یا

ارثبری از مدلهای جنگو چه حالاتی دارد

ارثبری از مدلهای جنگو بستگی به این نکته دارد که آیا مدل فرزند و مدل پدر هر دو دارای جدولهای دیتابیس جداگانه ای هستند یا خیر؟ آیا کلاس پدر فقط برای این است که یک سری فیلد و متد و استاندارد برای فرزندان تعریف کند و اصلا خودش جدول دیتابیس نیست یا نه؟ آیا پدر و فرزند هر دو جدول دیتابیس هستند و فقط ارتباط یک به یک یا چند به چند دارند؟ با توجه به این سوالات حالات متفاوتی وجود دارد

Abstract Base Model Inheretence

ارثبری abstract در این روش کلاس پدر یک سری پارامتد و متد تعریف میکند و فقط برای به ارث برده شدن توسط فرزندان است و اصلا خودش جدول دیتابیس ندارد و اصلا استفاده دیگری ندارد و از آن هیچ instance ای هم ساخته نمی شود.

Multi-table inheritance

در این روش هر دو کلاس دارای جدول دیتابیس هستند و جداگانه Manager دارند و استفاده می شوند. به صورت implicit بین آنها ارتباط OneToOne شکل میگیرد و می توان به همدیگر هم دسترسی داشت. می توان به صورت explicit آن ارتباط به ManyToMany تغییر داد.

Proxy Model

در این حالت کلاس فرزند پارامتد و داده جدیدی به کلاس پدر اضافه نمی کند و فقط رفتار کلاس پدر را تغییر می دهد یعنی متدهای جدیدی تعریف می کند نه اینکه فیلد اضافه کند. مثلا Manager پیشفرض را تغییر میدهد یا یک Manager دوم هم برای پدر تعریف می کند. در این حالت کلاس Proxy به دیتای پدر دسترسی دارد.

Multiple Inheretence

چطوری توی جنگو مدل رابطه IS-A ایجاد کنیم؟

فرض کنید یک مدل ساختیم به اسم Place که داری آدرس و مشخصات یک مکان رو داره اون وقت یک مدل میخایم بسازیم به اسم Restaurant خوب بنظر منطقیه که تمام اون فیلدهایی که توی Place تعریف کردیم رو اینجا هم تعریف کنیم؟ نه میشه از Inheritance استفاده کرد. یک راه بهتر ایجاد رابطه IS-A هست چون restaurant is a place و این کار رو با django.db.models.OneToOneField انجام میدیم. که البته با ارث‌بری هم میشد انجام داد در واقع Inheritance به صورت implicit یک IS-A relationship هست.

lazy relationship تو ORM جنگو

اگر توی یک کلاس یک رابطه با کلاس دیگه ای که هنوز تعریف نشده بسازیم بهش میگیم lazy relationship مثل

چون کلاس Book بالاتر تعریف شده بنابراین کلاس Publisher دیده نمیشه و اگه توی ForeignKey کلاس Publisher رو توی دابل کوتیشن نزاریم ارور میگیریم و اینکه گذاشتیم توی دابل کوت “” یعنی گفتیم هنوز تعریف نشده و به این میگن lazy relationship

Hook چیه؟

توی برنامه نویسی شی‌گرا یا OOP که میشه از ارث‌بری استفاده کرد یکی از قابلیتهایی که مخصوصا توی کدهای فریم ورک ها میبینید اینه که توی یک کلاس یک متد تعریف شده و بدنه متد خالیه و عملا این رو گذاشتن که کلاس فرزند بتونه اون رو overwrite بازنویسی کنه که یا قابلیتهایی بهش اضافه کنه یا رفتار متد رو تغییر بده که به متدها میگن Hook.

مثلا توی کلاس BaseModel توی جنگو که کلاس Django.db.models.Model از اون ارثبری کرده یه سری متد وجود داره که میشه بازنوسی کرد مثلا با بازنوسی متد save() وقتی که مدل قراره توی جدول دیتابیس ذخیره میشه، این تابع اجرا میشه که میتونیم توی کلاس مدل اون رو بازنویسی کنیم که رفتارش رو عوض کنیم مثلا قبل از اینکه ذخیره کنه یه خط لاگ هم بندازه. توی این کلاس hook های زیر هم هستن

pre_save()

post_save()

__init__()

delete()

pre_delete()

post_delete()

__str__()

,…