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

در این جلسه در رابطه با انواع روابط دیتابیسی توضیح میدهیم و آنها را به طور تخصصی و نحوه استفاده از آنها را در query بررسی می کنیم . با ما همراه باشید .آموزش جنگو : جلسه سی و یک | بررسی روابط دیتابیسی در نمونه های جنگوشی های ارجاع داده شدهدر این بخش به بررسی ساده ی نحوه استفاده از API فیلد های روابطی مانند ForeignKey و OneToOneField یا ManyToManyField خواهیم داشت .با استفاده از مثال های اول این مقالات ,برای مثال در مدل Entry می توانید با دسترسی به ویژگی blog (اگر شی Entry با عنوان e تعریف شود : e.blog) شی Blog مرتبط را دریافت کنید .البته شما این مباحث را قبلا بررسی کردید . ما در اینجا برای افراد کنجکاوتر این مباحث را با جزییات توضیح می دهیم .همچنین جنگو یک API دیگر برای طرف دیگر رابطه ایجاد می کند . پیوندی از مدل ارجاع داده شده به مدلی که رابطه را تعریف کرده است . برای مثال اگر یک شی Blog با نام b تعریف شده باشد ,از طریق entry_set می تواند به لیستی از تمامی شی های مدل Entry دسترسی یابد که به شی b رابطه دارند (دقیقا برعکس مثال بالایی). برای مثال : ()b.entry_set.allتمام مثال های این بخش را با مثال های مدل Blog و Author و Entry پیش خواهیم برد . (در سری مقالات پیش آنها را ایجاد کردیم . می توانید آنها را بررسی کنید)روابط One-to-many (نوع Forward)همانطور که می دانید اگر برای مثال یک ForeignKey داشته باشید (جزو روابط یک به چند حساب می شود) شما می توانید از طریق نام فیلد ForeignKey مدل ,به شی ارجاع داده شده (شی ای از مدل Blog که به آن رابطه زدیم) دسترسی پیدا کنید .برای مثال :>>> e = Entry.objects.get(id=2) >>> e.blog # Returns the related Blog object.همچنین شما از این روش می توانید حتی برای ست کردن (تنظیم شی جدید برای فیلد ForeignKey) استفاده کنید . فقط حواستان باشد که تا متد ()save را فراخوانی نکنید تغییرات روی دیتابیس اعمال نمی شوند . برای مثال :>>> e = Entry.objects.get(id=2) >>> e.blog = some_blog >>> e.save()اگر فیلد شما از null=True پشتیبانی کند (یعنی مقادیر NULL را بپذیرد) شما می توانید با تنظیم فیلد روی None رابطه را با آن شی حذف کنید . برای مثال :>>> e = Entry.objects.get(id=2) >>> e.blog = None >>> e.save() # &quotUPDATE blog_entry SET blog_id = NULL ...;&quotدسترسی های شما به فیلد ForeignKey برای اولین بار در کش ذخیره می شوند و دفعات بعدی دسترسی , از همان کش استفاده می کنند . برای مثال :>>> e = Entry.objects.get(id=2) >>> print(e.blog) # Hits the database to retrieve the associated Blog. >>> print(e.blog) # Doesn't hit the database; uses cached version.توجه داشته باشید که استفاده از متد ()select_related تمام کش را از قبل پر می کند (درباره این متد جلوتر صحبت خواهیم کرد) . برای مثال :>>> e = Entry.objects.select_related().get(id=2) >>> print(e.blog) # Doesn't hit the database; uses cached version. >>> print(e.blog) # Doesn't hit the database; uses cached version.نکته : نوع های رابطه ای که رو به جلو هستند (یعنی از مدل حاوی رابطه به شی ارجاع داده شده) Forward نامیده می شوند . بالعکس نوع های رابطه بازگشتی (یعنی از شی ارجاع داده شده به مدل حاوی رابطه) Backward نامیده می شوند . مثال های بالا تماما از نوع Forward بودند .بررسی روابط backwardهنگامی که شما در یک مدل رابطه ForeignKey ایجاد می کنید ,اشیا مدل ارجاع داده شده به یک مدیر یا manager دسترسی دارند که همه ی اشیا مدل اول یا منبع را (مدل حاوی رابطه) بازخواهد گرداند . به طور پیش فرض این مدیر FOO_set نام دارد . در آن FOO نام مدل منبع با حروف کوچک است . این مدیر ,یک QuerySet را بازمیگرداند . پس شما می توانید روی نتایج آن فیلتر انجام دهید (همانطور که در بخش قبلی توضیح داده شد).برای مثال :>>> b = Blog.objects.get(id=1) >>> b.entry_set.all() # Returns all Entry objects related to Blog. # b.entry_set is a Manager that returns QuerySets. >>> b.entry_set.filter(headline__contains='Lennon') >>> b.entry_set.count()شما می توانید FOO_set را با استفاده از تعریف related_name در ForeignKey بازنویسی کنید . برای مثال اگر کد را به شکل blog = ForeignKey(Blog , on_delete=models.CASCADE , related_name=‘entries’) بنویسید , مثال بالا به شکل زیر خواهد بود :>>> b = Blog.objects.get(id=1) >>> b.entries.all() # Returns all Entry objects related to Blog. # b.entries is a Manager that returns QuerySets. >>> b.entries.filter(headline__contains='Lennon') >>> b.entries.count()استفاده از یک reverse manager سفارشیبه طور پیش فرض مدیر یا manager مورد استفاده برای روابط معکوس یا بازگشتی یک زیر کلاس از مدل پیش فرض مدل است . اگر می خواهید برای یک query از مدیری سفارشی استفاده کنید از روش زیر استفاده می کنید:from django.db import models class Entry(models.Model): #... objects = models.Manager() # Default Manager entries = EntryManager() # Custom Manager b = Blog.objects.get(id=1) b.entry_set(manager='entries').all()اگر EntryManager فیلتر پیش فرض را در متد ()get_aueryset اعمال میکرد ,این عملیات برای متد ()all و همگی اشیا اجرا می شد .تعیین یک مدیر سفارشی همچنین به شما این امکان را می دهد تا از متد ها سفارشی سازی شده آن نیز استفاده کنید :b.entry_set(manager='entries').is_published()نکته : در قسمت های بعدی به شما توضیح می دهیم که چگونه می توانید یک مدیر سفارشی بنویسید .متدهای پرکاربرد در related objectsعلاوه بر تمام متد هایی که در بالا گفته شد یک ForeignKey دارای متد های دیگری نیز است که در اینجا نگاهی اجمالی به آنها انداخته ایم . خلاصه ای از هر یک در زیر آمده است و جزییات کامل را بعدا در فصل related obejcts خواهید خواند .متد add(obj1 , obj2) :شی های داده شده را به ForeignKey اضافه می کند .متد create(**kwargs) :یک شی جدید را ایجاد می کند و آن را به ForeignKey اضافه می کند . همچنین شی جدید را باز میگرداند .متد remove(obj1 , obj2) :شی های داده شده را از ForeignKey حذف می کند .متد ()clear :تمام اشیا را از فیلد شما پاک می کند .متد set(objs) :یک مجموعه از اشیا را جایگزین می کند .بعدا درباره تمام این متد ها بیشتر صحبت خواهیم کرد .روابط Many-to_manyدر این نوع از رابطه ,هر دو طرف رابطه یک API برای دسترسی به طرفین دیگر خود می گیرند . API مشابه عملگرد backward در one_to_many هست . گرچه دارای تفاوت هایی نیز هست .یک تفاوت در نامگذاری ویژگی ها (attribute) است . مدلی که ManyToManyField را درون خود تعریف می کند ,از نام متغییر حاوی فیلد برای آن استفاده می کند گرچه در رابطه reverse یا بازگشتی خود از نام مدل حاوی فیلد به صورت حروف کوچک به علاوه set_ استفاده می کند .برای مثال :e = Entry.objects.get(id=3) e.authors.all() # Returns all Author objects for this Entry. e.authors.count() e.authors.filter(name__contains='John') a = Author.objects.get(id=5) a.entry_set.all() # Returns all Entry objects for this Author.مانند روابط ForeignKey ,روابط ManyToMany نیز می توانند related_name را مشخص کنند . در مثال بالا, اگر فیلد ManyToManyField در مدل Entry ,یک ویژگی “related_name = entries” را مشخص کنید , هر شی از مدل Author به جای یک entry_set یک entries خواهد داشت .یک تفاوت دیگر ManyToMany با OneToMany این است که نوع اول می توانند در متد های ()add() , set و ()remove مقدار های primary (مانند لیست) دریافت کنند . برای مثال اگر e1 و e2 شی هایی از مدل Entry باشند ,آنگاه متد ()set می تواند به صورت زیر عمل کند .a = Author.objects.get(id=5) a.entry_set.set([e1, e2]) a.entry_set.set([e1.pk, e2.pk])روابط One-to-oneاستفاده از روابط One-to-one بسیار شبیه استفاده از Many-to-one است . برای مثال اگر شما یک فیلد OneToOneField در مدل خود داشته باشید ,شما می توانید با استفاده از نام متغییر حاوی فیلد به آن ویژگی دسترسی پیدا کنید :class EntryDetail(models.Model): entry = models.OneToOneField(Entry, on_delete=models.CASCADE) details = models.TextField() ed = EntryDetail.objects.get(id=2) ed.entry # Returns the related Entry object.تفاوت اصلی در میان این دو نوع از روابط در آن است که در OneToOneField برای استفاده از رابطه بازگشتی شما مانند هر رابطه دیگری به یک manager و API آن دسترسی دارید ,اما manager شما بجای بازگرداندن یک مجموعه از اشیا یک شی واحد را بازمیگرداند . (به دلیل اینکه رابطه شما فقط با یک شی می تواند باشد) برای مثال :e = Entry.objects.get(id=2) e.entrydetail # returns the related EntryDetail objectاگر هیچ شی ای هنوز به این فیلد اختصاص داده نشده باشد یک خطای DoesNotExist بازگردانده می شود . همچنین به یاد داشته باشید که می توانید در روابط بازگشتی One-to-one به همان روش عادی فیلد را مقداردهی کنید . (دقت کنید که گفتیم رابطه بازگشتی را!)e.entrydetail = edنکته : روابط بازگشتی در جنگو به وسیله تنظیمات INSTALLED_APPS شناسایی و ایجاد می شوند . پس مراقب باشید زیرا اگر اپ حاوی مدل را به درستی در جنگو وارد نکنید ,باعث می شود تا بعضی از روابط بازگشتی به درستی کار نکنند .(این یکی از ویژگی های خوب جنگو است زیرا بقیه فریموورک ها همواره نیاز دارند تا رابطه های بازگشتی را خودتان دستی تعریف کنید ولی جنگو این کار را اتوماتیک انجام می دهد)در این جلسه در رابطه با انواع روابط دیتابیسی جنگو و کاربرد آنها صحبت کردیم . در جلسه بعدی به بررسی پیشرفته query و چگونگی عملکرد آن می پردازیم .