آموزش جنگو (Basic) : جلسه بیست و شش | کلاس متا (meta)

در این جلسه به بررسی کلاس های متا در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو : جلسه بیست و شش | کلاس متا (meta)کلاس های Metaدر این قسمت تمامی گزینه هایی (متغییرها) که می توان در کلاس Meta برای یک مدل تعریف کرد را بررسی خواهیم کرد .کلاس Meta زیرکلاسی است که در مدل ایجاد می شود تا اطلاعات و تنظیمات اضافه ای را برای مدل به وسیله آن تعریف کنید .متغییر های قابل تعریف در کلاس های Meta1-متغییر اول که می خواهیم معرفی کنیم ,متغییر abstract است که دو مقدار True و False را قبول می کند . در گذشته استفاده از آن را توضیح دادیم و اشاره کردیم که استفاده از آن فقط برای ارث بری مدل ها است . (حتما به صورت کامل مباحث شی گرایی مدل ها را بخوانید)2-متغییر دوم شما app_label است . اگر مدل شما خارج از اپی که در INSTALLED_APPS وجود دارد ,ساخته شده است , باید مشخص کنید که این مدل به کدام اپ تعلق دارد . (نام اپ را به صورت رشته به عنوان مقدار می پذیرد)app_label = 'myapp'فایده این روش این است که فرمت فراخوانی مدل شما به صورت app_label.object_name یا app_label.model_name باشد ,شما می توانید در مواقع موردنیاز از فرمت های model._meta.label یا model._meta.label_lower استفاده کنید .3-متغییر سوم base_manager_name است . اگر به یاد داشته باشید گفتیم که در ()model.objects.all یا ()model.objects.filter نام objects در واقع به manager مدل شما اشاره دارد . این متغییر یک رشته را قبول می کند که نام همان objects است . در واقع نام base_manager شما در این متغییر تعریف می شود و به صورت پیش فرض objects است .نکته : اگر آن را مثلا به things تغییر دهید ,باید از این پس بنویسید : ()model.things.filter یا ()model.things.all 4-متغییر چهارم db_table است . این متغییر یک رشته را قبول می کند . در واقع نام جدول شماست که به صورت پیش فرض از روی نام مدل برداشته می شود . برای تنظیم نام جدول به music_album به صورت زیر در کلاس Meta بنویسید :db_table = 'music_album'نکته :برای صرفه جویی در زمان جنگو نام جدول ها را خودش از روی app_label و نام مدل شما ایجاد می کند . به این صورت که نام یک جدول دیتابیس تشکیل شده است از : ابتدا app_label و سپس نامی که برا ساخت اپ حاوی مدل در دستور manage.py startapp استفاده کردید با یک زیرخط یا underscore یا _ میان آنها ساخته می شود .برای مثال اگر یک اپ با نام bookstore داشته باشید و یک مدل با نام Book داشته باشید ,شما جدولی با نام bookstore_book خواهید داشت .برای بازنویسی نام جدول از db_table استفاده کنید .نکته جالب این است که اگر نام جدول پیشنهادی شما دارای کلمات رزرو شده SQL یا کلماتی است که در پایتون مجاز نیست , عیبی ندارد . جنگو آنها را در پشت صحنه پردازش خواهد کرد .بهتر است که از حروف کوچک برای MySQL و MariaDB استفاده کنید . همچنین در Oracle شما محدودیت 30 کاراکتری را برای نام جدول دیتابیس خواهید داشت . جنگو در این مواقع معمولا طول آنها را کوچک خواهد کرد و همه ی آنها را به حروف بزرگ تبدیل خواهد کرد . برای جلوگیری از این اتفاق تنها کافیست به صورت زیر عمل کنید : (آنها را میان “” , ‘’ قرار دهید)db_table = '&quotname_left_in_lowercase&quot'5-متغییر پنجم شما db_tablespace است . (مبحث db_tablespace را در مورد دیتابیس ها بخوانید) این متغییر یک رشته را قبول می کند و در واقع نام tablespace شما است . اگر آن را تعیین نکرده باشید (که معمولا نمی کنند) , جنگو آن را از تنظیم DEFAULT_TABLESPACE در settings.py بر میدارد . اگر دیتابیس شما از db_tablespace پشتیبانی نکند ,این متغییر نادیده گرفته می شود .6-متغییر ششم default_manager_name است . به یاد داشته باشید که base_manager با defaut_manager تفاوت دارد . Base_manager از همان ابتدا که مدل شما ایجاد می شود ,وجود دارد . Default_manager نیز در واقع مدیری است که جنگو از آن برای مدل استفاده می کند . این متغییر یک رشته را قبول می کند و در واقع همان نام default_manager شما است .به طور خلاصه از default_manager فقط در زمانی استفاده می شود که شما یک manager را خودتان ایجاد کرده باشید.7-متغییر هفتم default_related_name است . این متغییر یک رشته را قبول می کند و همانطرو که از اسمش مشخص است , نام related_nameهایی که رابطه بازگشتی آنها به این مدل است را تعیین می کند . (یعنی رابطه فیلد های آنها با این مدل است) . به صورت پیش فرض model_name_set است . (بجای model_name نام مدل قرار می گیرد)همچنین مقدار این متغییر برای related_query_name نیز استفاده خواهد شد .شما می دانید که related_name های شما باید unique و منحصر به فرد باشند .8-متغییر هشتم get_latest_by است . متد latest و earliest را بعدا خواهید خواند . توضیح ابتدایی برای آنها این است که فراخوانی این دو متد باعث می شوند که اشیای شما به ترتیبی مرتب شوند . یعنی ترتیب جدیدترین و قدیمی ترین . شما با استفاده از این متغییر به آنها می گویید تا بر اساس چه فیلدی جدیدترین و قدیمی ترین را مرتب کنند . نام فیلد مورد نظر را در رشته بنویسید یا اگر فیلدهایی دارد باید آنها را درون یک لیست مرتب کنید. معمولا فیلد های مورد استفاده برای این متد ها DateField , DateTimeField یا IntegerField است .برای مثال :# Latest by ascending order_date. get_latest_by = &quotorder_date&quot # Latest by priority descending, order_date ascending. get_latest_by = ['-priority', 'order_date']9-متغییر بعدی managed است . به صورت پیش فرض True است . بهتر است که آن را تغییر ندهید . این متغییر روی True قرار گرفته است و معنای آن این است که جنگو به صورت اتوماتیک migrateها را برای مدل شما می سازد و با دستور flush (بعدا آشنا می شوید) آنها را برای شما حذف می کند . وقتی این متغییر روی True باشد ,جنگو سیستم migrateها بر روی مدل را کنترل خواهد کرد . (چیزی است که شما معمولا می خواهید)اگر برابر با False قرار داده شود ,جنگو هیچ عملیات ایجاد جدول ,اصلاح یا حذف جدولی را بر روی این مدل انجام نمی دهد . این در صورتی مفید است که جدول از قبل به وسیله های دیگری (مانند خود برنامه نویس) ایجاد شده باشد . این تنها تفاوت برای managed=False است و تمام جنبه های دیگر از کار کردن با این مدل مانند روال عادی است . این یعنی :-اگر فیلد primary key وجود نداشته باشد , جنگو آن را به صورت خودکار در مدل ایجاد می کند . برای جلوگیری از سردرگمی هنگام استفاده از جدول های مدیریت نشده توسط جنگو ,بهتر است تمامی فیلد های جدول خود را در مدل تعریف کنید (ایجاد کنید).-اگر شما یک ManyToManyField در مدلی که managed=False است داشته باشید که به مدل دیگری اشاره می کند که آن مدل نیز ویژگی managed=False را دارا است ,جدول میانی به صورت اتوماتیک برای آنها ایجاد نمی شود .نکته : با این حال اگر جدول میانی بین دو مدل که فقط یکی از آنها managed=False را دارا است باشد ,جدول میانی را خود جنگو مانند روال عادی ایجاد خواهد کرد .نکته : اگر نیاز دارید تا دو مدل مدیریت نشده (managed=False) را به یکدیگر از ManyToManyField متصل کنید,فقط کافی است جدول میانی را دستی ایجاد کنید و در فیلد خود از through برای ربط دادن به مدل سفارشی استفاده کنید .برای ایجاد تست ها روی جداول مدیریت نشده نیز به شما بستگی دارد تا آنها را در تست به کار ببرید و یا جداول را به صورت صحیح از مدل خود ایجاد کنید .اگر نیز می خواهید رفتار مدل را در سطح خود پایتون تغییر دهید می توانید یک مدل مدیریت نشده ایجاد کنید و سپس از آن یک کپی نیز ایجاد کنید . البته راهکار بهتر استفاده از مدل های نوع proxy است .10-متغییر بعدی order_with_respect_to است . این متغییر یک رشته را به عنوان مقدار می پذیرد , که نام یکی از فیلد های مدل است . این گزینه ,شی های مدل را با توجه به آن فیلد مرتب می کند . بر فرض مثال که شما دو مدل با نام های Answer (جواب) و Question (سوال) دارید . اگر هر سوال چند جواب داشته باشد ولی هر جواب فقط متعلق به یک سوال باشد و ترتیب جواب ها برای شما اهمیت داشته باشد ,کد زیر را خواهید نوشت .from django.db import models class Question(models.Model): text = models.TextField() # ... class Answer(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) # ... class Meta: order_with_respect_to = 'question'هنگامی که این متغییر تنظیم می شود ,دو متد برای دریافت و تنظیم اشیا مرتبط اضافه خواهد شد . ()get_RELATED_order و ()set_RELATED_order متد های شما هستند که در آنها RELATED با نام مدل به صورت حروف کوچک جایگزین می شود . به عنوان مثال , با فرض اینکه شی ای از Question دارای چندین پاسخ یا همان Answer های مرتبط باشد ,لیستی که از این متد دریافت خواهد شد ,حاوی id های اشیا Answer به صورت ترتیبی است.>>> question = Question.objects.get(id=1) >>> question.get_answer_order() [1, 2, 3]حال که فهمیدید چگونه می توانید آنها را دریافت کنید ,بهتر است نگاهی بیاندازیم به اینکه چطور ترتیب آنها را تنظیم کنیم . شما به وسیله کد زیر و فراخوانی این متد می توانید لیستی حاوی ایدی Answer های مرتبط با Question را به این متد بدهید تا آنها را به ترتیب خودتان مرتب کند .>>> question.set_answer_order([3, 1, 2])حال که فهمیدید چگونه می توان به طور کلی اشیا Answer مربوط به هر شی Question را دریافت کرد یا تنظیم نمود ,باید بدانید که هر کدام از Answer ها نیز دارای دو متد خواهند شد . یکی از آنها ()get_next_in_order است (شی بعدی را در ترتیب نشان می دهد) و بعدی ()get_previous_in_order است (شی قبلی را در ترتیب نشان می دهد) . از آنها برای دسترسی به Answer ها در ترتیب خودشان استفاده می توانید کنید . با فرض اینکه Answer های شما بر اساس فیلد id مرتب شده اند :>>> answer = Answer.objects.get(id=2) >>> answer.get_next_in_order() <Answer: 3> >>> answer.get_previous_in_order() <Answer: 1>در پشت صحنه ,اضافه کردن متغییر order_with_respect_to یک فیلد ستون اضافه در دیتابیس اضافه خواهد کرد که جنگو ترتیب اشیا را در آن نگه داری می کند (نام ستون order_ است) . پس در نتیجه نمی توانید ordering و order_with_respect_to را با هم استفاده کنید . همچنین استفاده از order_wth_respect_to به این منظور است که هر گاه از متد های آن (مانند get_answer_to) استفاده کنید ,اشیا به ترتیب به شما نمایش داده می شود ولی ordering همیشه آنها را مرتب خواهد کرد .نکته : بعد از اینکه این متغییر اضافه شد ,حتما برای ایجاد ستون جدیدش در دیتابیس عملیات migrate را انجام دهید . (همان دستور python3 manage.py migrate)11-متغییر بعدی در لیست ما ordering نام دارد . Ordering ترتیب نگه داری اشیا را بر اساس یک فیلد یا چند فیلد ذخیره خواهد کرد . تفاوت آن با متغییر قبلی این است که ordering اشیا را همیشه مرتب نگه می دارد و بدون استفاده از متد خاصی شما اشیای مدل را مرتب شده دریافت می کنید .ordering = ['-order_date']این یک تاپل یا لیستی از رشته ها است (می تواند یک expression نیز باشد) . هر رشته نام یک فیلد از مدل شما باید باشد . علاوه بر این هر رشته می تواند یک – داشته باشد تا ترتیب مرتب شدن بر اساس آن فیلد نزولی باشد . فیلد های بدون علامت – با ترتیب صعودی نمایش داده می شوند . اگر می خواهید به صورت رندوم اشیا مرتب شده باشند از ? در رشته استفاده کنید .برای مثال ,برای مرتب کردن اشیا یک مدل ,بر اساس فیلد pub_date و ترتیب صعودی از این استفاده کنید :ordering =['pub_date']حال برای مرتب سازی بر اساس فیلد pub_date و ترتیب نزولی به صورت زیر اقدام کنید :ordering = ['-pub_date']حال برای مرتب سازی بر اساس فیلد pub_date (به صورت نزولی) و فیلد author (به صورت صعودی) به صورت زیر اقدام کنید :ordering =['-pub_date', 'author']شما می توانید از query expressions نیز استفاده کنید (جلوتر خواهید خواند) . برای مرتب سازی بر اساس فیلد author به ترتیب صعودی و به طوری که مقادیر null در آخر مرتب شوند ,به صورت زیر اقدام کنید .from django.db.models import F ordering = [F('author').asc(nulls_last=True)]نکته : به یاد داشته باشید که فیلد هایی که در ordering استفاده می کنید باید معمولا unique و منحصر به فرد باشند .برای مثال اگر فیلد name منحصر به فرد یا unique=True نباشد ,و شما دو مقدار یکسان را درون دو شی متفاوت ذخیره کرده باشید , جنگو تضمین نمی کند که هر بار با همان ترتیب قبلی به شما آنها را بازگرداند .12-متغییر بعدی permissions است . جنگو برای مدل های شما یک جدول با نام permissions می سازد . مجوزهای افزودن ,حذف ,تغییر و مشاهده برای هر مدل به صورت اتوماتیک اضافه می شود . گرچه بحث مجوز ها را کمی سخت می توان توضیح داد ,اما می توانید برای مثال نگاهی به مدل User و مجوز های آن بیندازید (در ادمین پنل قسمت users و قسمت permissions).در مثال زیر یک مجوز اضافی تحت عنوان can_deliver_pizza را به مدل اضافه می کنیم :permissions =[('can_deliver_pizzas', 'Can deliver pizzas')]مقدار هایی که این متغییر قبول می کند یک لیست حاوی تاپل های دو عضوی است . عضو اول مقدار ذخیره سازی شد در دیتابیس و عضو دوم مقداری است که به کاربر نمایش داده خواهد شد .13-متغییر بعدی default_permissions است . مجوز های پیش فرض جنگو برای هر مدل شامل (‘add’ , ‘change’ , ‘veiw’ , ‘delete’) است . شما ممکن است بخواهید این مجوز ها را بازنویسی کنید . برای مثال اگر اپ شما هیچ کدام از مجوز ها را لازم نداشته باشد ,این متغییر را با یک لیست خالی مقدار دهی می کنید . به یاد داشته باشید این فرایند باید قبل از انجام migrate بر روی مدل انجام شود تا از صدور هر گونه مجوز برای مدل جلوگیری شود .14-متغییر بعدی شما proxy است . این متغییر برای مباحث ارث بری استفاده می شود . (در قبل تر توضیح دادیم)15-گزینه بعدی required_db_features است . مقدار خود را به صورت لیست قبول می کند . لیستی از قابلیت های پایگاه داده (database features) که اتصال فعلی شما باید داشته باشد (در مرحله migrate آنها را اجرا کند) . برای مثال اگر مقدار این متغییر را [‘gis_enabled’] تنظیم کنید ,مدل در پایگاه های داده مجهز به GIS میشود (مباحث دیتابیس ها) . همچنین برای نادیده گرفتن برخی از مدل ها در هنگام تست کردن با چند دیتابیس (مبحث چند دیتابیسی در جنگو را بعدا می خوانید) کارایی دارد .15-گزینه بعدی required_db_vendor است . نام یک vendor برای دیتابیس است که باید توسط جنگو پشتیبانی شود. مقدار هایی که می توانید در قالب یک رشته به این متغییر بدهید , sqlite و mysql و postgresql و oracle هستند . اگر این ویژگی و vendor فعلی شما در اتصال به دیتابیس باهم یکسان نباشند ,مدل همگام سازی نمی شود ! (vendor جزو مباحث دیتابیس ها است)16-متغییر بعدی select_on_save است . دو مقدار False و True را می پذیرد . تعیین می کند که آیا جنگو باید از نسخه pre-1.6 متد ()save استفاده کند یا خیر . نسخه قدیمی این متد ,ابتدا دستور SELECT را بر روی ستونی اعمال می کند تا از وجود آن اطمینان حاصل کند و سپس آنها را آپدیت می کند . متد جدید , UPDATE را به صورت مستقیم اعمال می کند و اگر خطایی وجود داشت آن را باز می گرداند . در بعضی از موارد نادر , UPDATE روی ستون های موجود به جنگو نشان داده نمی شود . برای مثال در PostgrSQL دستور ON UPDATE مقدار Null را بازمیگرداند . در چنین مواردی ,متد جدید دستور INSERT را انجام می دهد حتی اگر ستون از قبل وجود داشته باشد (و شما این را نمی خواهید).معمولا این موارد نادر هستند و بهتر است پیش فرض (False) این گزینه را دستکاری نکنید .17-بعدی indexes است که در گذشته آن را توضیح دادیم !18-متغییر بعدی unique_together است . مقدار یک لیست را که حاوی لیست های دیگری است را قبول می کند . هر لیست درون لیست اصلی حاوی نام فیلد هایی است که در کنار یکدیگر باید unique یا منحصر به فرد باشند .unique_together = [['driver', 'restaurant']]این عملیات در سطح دیتابیس و ادمین جنگو اعمال می شود . یعنی عبارت UNIQUE مناسب در CREATE TABLE گنجانده می شود . (معمولا از UniqueConstraint یا Constraint استفاده می شود)برای راحتی بیشتر می توانید فقط از یک لیست استفاده کنید اما این زمانی است که فقط با یک سری مجموعه از فیلد ها کار داشته باشید . (یعنی فقط همان فیلد ها با یکدیگر منحصر به فرد شوند و نه دو مجموعه از فیلد ها)unique_together = ['driver', 'restaurant']یک ManyToManyField را نمی توانید در این متغییر استفاده کنید (زیرا معنای خاصی ندارد!) . اگر نیاز دارید تا منحصر به فرد بودن اشیا ManyToMany را بررسی کنید باید از سیگنال ها (در جلوتر می خوانید) یا از through برای مدل استفاده کنید .اگر شما منحصر به فرد بودن فیلد ها را رعایت نکنید ,جنگو به شما ارور ValidationError را باز میگرداند.19-گزینه بعدی index_together است . (گرچه معمولا از همان indexes استفاده می شود) لیستی از لیست هایی را قبول می کند که هر لیست حاوی فیلد هایی است که باید با یکدیگر index شوند .index_together =[ [&quotpub_date&quot, &quotdeadline&quot], ]این لیست از فیلد ها با هم index خواهند شد . (عبارت CREATE INDEX مناسب برای آنها اعمال می شود)اگر شما فقط با یک مجموعه از فیلد ها کار دارید ,برای راحتی بیشتر از یک لیست استفاده کنید .index_together = [&quotpub_date&quot, &quotdeadline&quot]20-بعدی constraints است . لیستی از constraintها (بعدا می خوانید) که می خواهید بر روی مدل اعمال شوند . روش استفاده از آنها به صورت زیر است .from django.db import models class Customer(models.Model): age = models.IntegerField() class Meta: constraints = [ models.CheckConstraint(check=models.Q(age__gte=18), name='age_gte_18'), ]21-گزینه بعدی verbose_name است . این یک رشته را می پذیرد که نام قابل خواندن برای کاربر برای شی مفرد است .verbose_name = &quotpizza&quotاگر این گزینه داده نشود ,جنگو از یک نام munged از روی نام مدل رشته را ایجاد می کند . مثلا CamelCase به camel case تبدیل می شود .22- گزینه بعدی verbose_name_plural است . این رشته ای است که برای هر شی جنگو در نظر می گیرد . (نام آن)verbose_name_plural = &quotstories&quotاین گزینه در صورت داده نشدن ,روی verbose_name + ‘s’ تنظیم می شود.در این جلسه به طور کامل تمامی تنظیماتی که میتوانید در یک کلاس متا برای مدل خود انجام بدهید را بررسی کردیم . در جلسه بعدی به بررسی کوئری ها (query) در جنگو خواهیم پرداخت .