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

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

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__()

,…

مثال پایتون – تکه کد – سوال مصاحبه

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

خوب خروجی کد زیر:

خروجی کد یه جوریه که انگار پایتون شیشه زده :)))) حالا چرا؟

چند تا بحثه:

1.توابع توی پایتون خود آبجکت رو به عنوان پارامتر میگیرن نه کپی اونها رو:

ینی چی؟ وقتی ما به تابع test یه لیست و یه integer پاس دادیم، کپی اون لیست و integer به تابع داده نشده بلکه خود اون آبکت داده شده یعنی reference اون داده شده پس اگه توی تابع test اون پارامتر رو تغییر بدی خود اون آبجکت رو تغییر دادی. به بیان ساده تر اون l1 همون a هست نه کپی اون. انگار توی خانه حافظه اون لیست یه برچست جدید روش خورده و حالا با اون برچست جدید صداش میزنیم توی test. پس پارامترها توی توابع پایتون اینجوری پاس داده میشن.

2.توی پایتون list یک mutable هست و integer یک immutable

سوالی که پیش میاد اینه که چرا c تغییر نکرد پس؟ مگه اونجا هم دقیقا همون c را ندادیم به test؟ چرا اون c که توی test هست دقیقا همونی هست که توی main هست اما بعد از اینکه یه مقدار به c اضافه کردیم c+=1 دیگه از اونجا به بعد همون c قبلی نیست. این یه بحث جدیده به اسم mutable و immutable بودن توی پایتون.

توی پایتون list یک mutable هست یعنی قابل تغییره اما یک integer یک immutable یعنی غیرقابل تغییر است بنابراین وقتی c+=1 میکنیم اون c قبلی که برابر با 1 بود از حافظه پاک میشه و یه مقدار جدید توی جای جدیدی از حافظه ساخته میشه با مقدار 2 و به c وصل میشه (توی این لینک توضیحش هست)

یه مثال جالب توی پایتون – تکه کد

خروجی تکه کد زیر چیه؟

خروجی این کد 2 عدده که با هم متفاوت هستن یعنی توی پایتون اگر یه متغیر integer یا string تغییر کنن (مثلا یه واحد بهشون اضافه بشه یا یه کاراکترش حذف بشه) دیگه اون متغیر قبلی نیست و عملا اون قسمت حافظه که متغیر قبلی گرفته آزاد میشه و یه قسمت جدید allocate میشه و مقدار جدید توی اون ذخیره میشه و به a بایند میشه. اینه که گفته میشه توی پایتون integer و string ها immutable هستن.

توی مثال بالا وقتی a+=1 صدا اجرا میشه متغیر قبلی پاک میشه و یه متغیر جدید توی حافظه ساخته میشه با آدرس جدید و مقدار جدید ینی 2 بهش داده میشه و این خانه حافظه به a داده میشه.

حالا کد زیر چطور میشه؟

خروجی این کد همینطور که میبینید با قبلی فرق داره این بار خروجی 1 عدده. چون list توی پایتون mutable هست و وقتی یک لیست رو تغییر میدیم دیگه لیست جدید ساخته نمیشه همون قبلی توی همون جای قبلی حافظه تغییر کرده.

لینک های بدرد بخور:

لینک 1

لینک 2

Coupling یا وابستگی -سوال مصاحبه

توی یک مصاحبه سوال زیر از من پرسیده شد که به درستی نتونستم جواب بدم ولی بعدا توی مبحث Refactoring جوابش رو پیدا کردم.

سوال:

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

جواب:

توی بحث Refactoring یکی از مواردی که باید بهش توجه بشه Bad smell های کد هست که یکی از اونها coupling زیاد توی کد هست. یعنی ارتباط زیاد کلاسهای متفاوت با همدیگر به صورتی که مدیریت و تغییر کد را سخت کند.

توی این لینک توضیح داده که اگر یک متد از یک کلاس از دیتای کلاس دیگه استفاده کنه بهش Featuer Envy میگن.

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

این یک پاسخ کلی بود در مورد App های جنگو هم داستان همینه. هر App از نظر مفهومی باید جدا از App های دیگه باشه و هرچی لازم داره توی خود App موجود باشه. اما توی مستندات جنگو یه نکته ای گفته شده که نباید فراموش بشه، رابطه 2 تا مدل که هر کدوم توی یک App جداگانه هستند ممکنه و ایرادی نداره که البته از نظر مفهموی این موردی نداره مثل:

لینک زیر راه حل هایی که نوشتم رو بهتر توضیح داده

لینک 1

لینک 2

ساخت کامند کاستوم در django-admin

توی جنگو میشه برای یک app کامند کاستوم custom command نوشت. use case های زیادی میتونه داشته باشه. فرض کنیم یه model داریم به اسم campaign که مربوط به قرعه کشی هایی هست که توی وبسایت فروشگاهمون وجود داره. این کمپین فقط عید نوروز فعاله و یه فیلد داره به اسم active که برابر با true میشه. حالا بعد از نوروز میخایم اون رو غیرفعال کنیم بهتره که بتونیم با یه کامند این تغییر رو توی دیتابیس بدیم.

برای این کار یک کامند برای campaign app میسازیم. از این به بعد هر پروژه ای که این app را به INSTALLED_APP اضافه کنه این کامند رو هم خواهد داشت. برای این کار باید توی app مورد نظر یه package با ساختار زیر بسازیم.

توی فایل campaigneactivation.py باید یه کلاس به اسم Command بسازیم که از کلاس BaseCommand ارث بری میکنه.

این کامند الان اضافه شده و میشه به شکل زیر ازش استفاده کرد.

کلاس BaseCommand پارامترهای زیادی داره که جزئیاتش تو این لینک هست