آموزش جنگو : جلسه سی و نه | بررسی API روابط دیتابیسی در جنگو

در این جلسه api مربوط به روابط دیتابیسی را بررسی خواهیم کرد . با ما همراه باشید .آموزش جنگو : جلسه سی و نه | بررسی API روابط دیتابیسی در جنگواشیا روابطیبا اینکه از قبل توضیحاتی را درباره اشیا مرتبط (اشیایی که در آنها روابط وجود دارد) داده ایم ,اما بهتر است تا یک بخش کامل را به بررسی دوباره و تکمیلی آن بپردازیم تا بهتر در ذهن شما جا بیافتد .جنگو دارای یک کلاس با نام RelatedManager است . این کلاس شامل متد هایی است که برای ایجاد و دریافت ارتباطات میان مدل ها به کار می روند . کلاس RelatedManager یک manager نیز محسوب می شود. برای روابط one-to-many و many-to-many می تواند استفاده شود ,ولی فقط در دو صورت کار می کند :1-متد ها در آن سوی دیگر یک ForeignKey قابل دسترسی هستند . برای مثال :from django.db import models class Blog(models.Model): # ... pass class Entry(models.Model): blog = models.ForeignKey(Blog, on_delete=models.CASCADE, null=True)در مثال بالا ,متدها در manager ای با نام blog.entry_set قابل دسترس هستند .2-در دو سوی یک ManyToManyField قابل دسترس هستند . برای مثال :class Topping(models.Model): # ... pass class Pizza(models.Model): toppings = models.ManyToManyField(Topping)در مثال بالا در چند manager با نام های topping.pizza_set و pizza.toppings متد ها , قابل دسترسی هستند .حال بیایید به بررسی متد ها بپردازیم . این متد ها می توانند از چندین manager که نام بردیم فراخوانی شوند . برای شما روی فیلد یک سری از تغییرات را اعمال می کنند و فقط در فیلد هایی وجود دارند که از نوع روابطی باشند . (دو نسخه synchronous و asynchronous آنها نیز در لیست زیر وجود دارد)1-متد add(*objs, bulk=True, through_defaults=None)یا aadd(*objs, bulk=True, through_defaults=None) : این متد اشیای دریافت شده را (objs*) به لیست اشیا درون فیلد اضافه می کند (رابطه ایجاد می کند) . برای مثال :>>> b = Blog.objects.get(id=1) >>> e = Entry.objects.get(id=234) >>> b.entry_set.add(e) # Associates Entry e with Blog b.در مثال بالا (حالتی که از یک ForeignKey استفاده کرده ایم) ,متد update که در مباحث QuerySet توضیح دادیم ,اجرا خواهد شد تا آپدیت جدید را انجام دهد و رابطه ای را میان شی یک و دو برقرار کند . البته باید اشیا از قبل ذخیره شده باشند .اگر از آرگومان bulk=False استفاده کنید ,می توانید مدیر مربوطه را مجبور کنید تا بجای متد QuerySet.update از save برای آپدیت استفاده کند (e.save) .به یاد داشته باشید که استفاده از add در یک رابطه many-to-many به هیچ وجه نمی گذارد تا save فراخوانی شود . این یعنی در این گونه از رابطه آرگومان bulk=False کار نمی کند . در این روابط فرایند آپدیت توسط ایجاد شدن روابط با متد QuerySet.bulk_create صورت می گیرد . اگر نیاز به شخصی سازی روش کار add برای روابط many-to-many دارید ,کافی است با سیگنال m2m_changed کار کنید . این سیگنال و روش کار با آن را بعدا توضیح می دهیم (pre_add و post_add نیز جزو مباحث کار با این سیگنال هستند) .نکته : استفاده از add در رابطه ای که از قبل وجود داشته باشد ,روابط را کپی نمی کند اما سیگنال ها را ارسال می کند.در روابط many-to-many نیز ,متد add اشیا مدل یا مقدار فیلد های آنها (معمولا primary key آنها) را در آرگومان objs* می پذیرد .آرگومان through_defaults نیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callable مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .2-متد create(through_defaults=None, **kwargs)یا acreate(through_defaults=None, **kwargs) : این متد یک شی جدید می سازد ,آن را ذخیره می کند و سپس با آن شی رابطه ایجاد می کند . نتیجه را با بازگرداندن شی ساخته شده ,نشان می دهد . برای مثال :>>> b = Blog.objects.get(id=1) >>> e = b.entry_set.create( ... headline=&quotHello&quot, body_text=&quotHi&quot, pub_date=datetime.date(2005, 1, 1) ... ) # No need to call e.saveat this point -- it's already been saved.کد بالا را می توان به شکل زیر نیز نوشت :>>> b = Blog.objects.get(id=1) >>> e = Entry(blog=b, headline=&quotHello&quot, body_text=&quotHi&quot, pub_date=datetime.date(2005, 1, 1)) >>> e.save(force_insert=True)توجه داشته باشید که نیازی نیست تا kwargs** را در متد مقداردهی کنید . kwargs** برای تعیین اینکه رابطه با چه شی ای ایجاد شود به کار می رود . در مثال بالا ما پارامتر blog را برای create ارسال نکردیم . جنگو خودش متوجه می شود که باید فیلد blog در شی جدید از مدل Entry روی متغییر b(که یک شی است) تنظیم شود .آرگومان through_defaultsنیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callableها مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .3-متد remove(*objs, bulk=True) یا aremove(*objs, bulk=True) :یک شی را دریافت می کند و آن را از اشیا مرتبط آن فیلد حذف می کند (یعنی رابطه میان آنها را حذف می کند) . برای مثال :>>> b = Blog.objects.get(id=1) >>> e = Entry.objects.get(id=234) >>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.مانند add , e.save نیز در مثال بالا برای انجام آپدیت فراخوانی شده است . با این حال استفاده از remove در یک رابطه many-to-many ,با متد QuerySet.delete اشیا مرتبط را حذف می کند که به این معنی است که هیچگاه متد save فراخوانی نمی شود . برای شخصی سازی فرایند کار متد remove ,با سیگنال m2m_changed کار کنید .آرگومان objs* , همان آرگومانی است که اشیایی که باید حذف شوند (مانند مثال بالا) را در آن قرار می دهید .برای ForeignKey ,این متد فقط زمانی می تواند مورد استفاده قرار بگیرد که null=True در فیلد تنظیم شده باشد. اگر فیلد مربوطه را نتوان روی None (Null) تنظیم کرد ,نمی توان یک شی را بدون اضافه شدن به یک رابطه دیگر , از یک رابطه حذف کرد . در مثال بالا ,حذف eاز b.entry_set معادل e.blog=None است و چون فیلد دارای null=True نیست ,اینکار انجام نمی شود .برای ForeignKey این متد یک آرگومان با نام bulk نیز دریافت می کند . اگر Trueباشد (پیش فرض) ,عملیات شما با QuerySet.update انجام می شود . اگر آن را برابر با False تنظیم کنید ,متد save روی هر شی مجزا فراخوانی می شود . گرچه قرار دادن آن روی False کمی عملکرد را سنگین تر می کند و سیگنال های pre_save و post_save را اجرا می کند .برای روابط many-to-many آرگومان bulk قابل دسترس نیست .4-متد clear(bulk=True) یا aclear(bulk=True) :این متد روابط همه ی اشیا مرتبط با فیلد را حذف می کند (دیگر با آن مدل رابطه نخواهند داشت). برای مثال :>>> b = Blog.objects.get(id=1) >>> b.entry_set.clearتوجه داشته باشید که این اشیا را حذف نمی کند ,فقط رابطه آنها را با مدل پاک می کند .درست مانند remove , clear نیز فقط زمانی می تواند در فیلد های ForeignKey مورد استفاده قرار بگیرد که null=True در آن فیلد تنظیم شده باشد . همچنین آرگومان bulk را نیز در این متد می توانید استفاده کنید .گرچه برای روابط many-to-many ,آرگومان bulk وجود ندارد .5-متد set(objs, bulk=True, clear=False, through_defaults=None) یا aset(objs, bulk=True, clear=False, through_defaults=None) : این متد برای شما اشیا مرتبط را با لیستی از اشیا جدید که به آن می دهید ,جایگزین می کند . برای مثال :>>> new_list = [obj1, obj2, obj3] >>> e.related_set.set(new_list)متد set , یک آرگومان با نام clear دارد . اگر برابر با False باشد (پیش فرض) ,اشیا گمشده از لیست ورودی ,به وسیله متد remove حذف می شوند و فقط اشیا جدید اضافه می شوند . اگر clear برابر با False باشد ,متد clear بجای آن فراخوانی می شود و کل مجموعه اشیا به یکباره اضافه می شوند .برای فیلدهای ForeignKey ,آرگومان bulk نیز وجود دارد که مقادیر آن به add یا remove در حال استفاده ارسال می شود .برای روابط many-to-many آرگومان bulk غیر قابل دسترسی است .به یاد داشته باشید که استفاده از set باعث ایجاد شرایط race می شود . برای مثال ممکن است بین فراخوانی clear و add ,یک سری از اشیا جدید نیز به دیتابیس اضافه بشوند .آرگومان through_defaults نیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callable مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .نکته : تمام متد های add , aadd , create , acreate, remove , aremove , clear , aclear , set , aset همگی تغییرات دیتابیس را بلافاصله اعمال می کنند و نیازی به فراخوانی save برای هر دو طرف رابطه نیست .در ضمن اگر از متد prefetch_related استفاده کنید ,متد های add و aadd و remove و aremove و clear و aclear و set و aset کش ذخیره شده را حذف می کنند .در این جلسه در رابطه با چگونگی کار کردن روابط دیتابیسی در جنگو صحبت کردیم . در جلسه بعدی در رابطه با migrations صحبت خواهیم کرد .