
در پایگاه داده (دیتابیس) بین موجودیت های ما روابط مختلفی می تواند برقرار باشد که با استفاده از کلید خارجی می توان این روابط را پیاده سازی کرد. (موجودیت هر چیزی است که ارزش نگه داری داشته باشد که ما برای نگه داری آنها داخل پایگاه داده جدول ایجاد می کنیم. به عنوان مثال کاربر ها، محصولات، دسته بندی ها و ...)
اکنون حالت های مختلف این روابط را بیان می کنیم و برای هر کدام مثالی را با هم بررسی می کنیم.
1. رابطه ی یک به یک : مثل رابطه ی شخص با کارت ملی، هر شخص فقط یک کارت ملی دارد و هر کارت ملی نیز تنها مخصوص یک نفر است.
2. رابطه ی یک به چند و رابطه ی چند به یک : مثل رابطه ی شخص با حساب بانکی، هر شخص می تواند چند حساب بانکی داشته باشد اما هر حساب بانکی فقط می تواند متعلق به یک شخص باشد.
3. رابطه ی چند به چند : مثل رابطه ی معلم با دانش آموز، هر دانش آموز می تواند چندین معلم داشته باشد و همچنین هر معلم نیز می تواند آموزگار چندین دانش آموز باشد. هدف این پست آشنایی با پیاده سازی این رابطه در جنگو می باشد.
قبل از ادامه ی مطلب اجازه دهید چند مثال دیگر را برای رابطه چند به چند با هم بررسی کنیم.
1. رابطه ی بین یک پست و کاربرانی که آنرا پسندیده اند: یک کاربر می تواند چند پست را پسندیده باشد و همچنین یک پست نیز می تواند چندین کاربر داشته باشد که آنرا پسندیده اند.
2. رابطه ی بین برچسب و مقاله: یک مقاله می تواند چندین بر چسب داشته باشد و همچنین هر بر چسب نیز می تواند در چندین مقاله استفاده شده باشد.
برای برقراری رابطه ی چند به چند در پایگاه داده، یک جدول واسط یا bridge table ایجاد می کنند که حداقل دارای سه ستون است.
1. کلید اصلی (در تمامی جداول باید موجود باشد و این جدول نیز از این قاعده مستثنا نیست)
2. کلید خارجی به موجودیت اول
3. کلید خارجی به موجودیت دوم

تصویر بالا نمونه ای از پیاده سازی رابطه ی چند به چند بین دانشجو و استاد می باشد که در اینجا جدول student_teacher جدول واسط ما می باشد و رابطه ی چند به چند میان جداول student و teacher توسط آن برقرار می شود. همچنین در این جدول فیلد id به کلید اصلی، فیلد student_id به کلید خارجی و به جدول student و فیلد teacher_id به کلید خارجی و به جدول teacher اختصاص دارد.
حال اجازه دهید طبق جدول رابطه ی چند استاد و دانشجو، مثال هایی را با هم بررسی کنیم.
1. استاد با id = 23 (استاد احمدی) با دانشجویان با id های 1 و 5 و 10 (اصغری و عارفی و مهدوی) کلاس دارد.
2. دانشجو با id = 1 (دانشجو اصغری) با اساتید با id های 23 و 44 درس دارد. (احمدی و علوی).
در قسمت قبلی با اینکه در دیتابیس چگونه رابطه ی چند به چند پیاده سازی می شود آشنا شدیم. خبر خوش این هست که جنگو بصورت خودکار برای ما جدول واسطه را ایجاد می کند و ما احتیاجی به ایجاد مدل برای جدول واسطه نداریم.
اکنون فرض کنید قصد داریم بخش محصولات مورد علاقه را در یک سایت فروشگاهی با جنگو پیاده سازی کنیم. رابطه ی بین محصول و کاربرانی که محصولی را به علاقه مندی های خود اضافه می کنند یک رابطه ی چند به چند است، زیرا هر کاربر می تواند چندین محصول را به علاقه مندی های خود اضافه کند و همچنین هر محصول می توان مورد علاقه ی چندین کاربر باشد.
برای پیاده سازی رابطه ی چند به چند در جنگو از ManyToManyField استفاده می کنیم.
برای ایجاد رابطه چند به چند میان محصول و کاربر قطعه کد زیر را به مدل Product اضافه می کنیم. در رابطه ی چند به چند زیاد فرقی نمی کند که ما many to many را در مدل User تعریف کنیم یا مدل Product. اما best practice آن است که ما در مدل هایمان اطلاعات نامربوط نگه نداریم و همچنین تا جای ممکن بین app هایمان وابستگی ایجاد نکنیم, بطور مثال مدل یوزر محل ذخیره اطلاعاتی نظیر نام, نام کاربری و .. هست و نباید از محصول چیزی در آن وارد شود و همچنین نباید اپ accounts را به اپ products وابسته کنیم پس منطقی تر است که این قطعه کد در مدل Product باشد.
قطعه کد داخل مدل Product
user_favorites = models.ManyToManyField(
get_user_model(),
related_name='favorite_products'
)
فراموش نشود که null=True بودن در ManyToManyField بی معنا است. البته این به این معنی نیست که این مقدار همیشه اجباری است، بلکه به این معنی است که این آپشن برای این فیلد بی معنی است زیرا برای ایجاد رابطه ی چند به چند در جدول کاربر یا محصول فیلدی اضافه نمی شود که بخواهیم تصمیم بگیریم می تواند مقدار null بپذیرد یا خیر بلکه تمام روابط صرفا با درج یک ردیف در جدول واسط ایجاد می شوند. اما برای اینکه در فرم ها مجبور به پر کردن این قسمت نشوید می توانید از blank=True استفاده کنید.
اگر بخواهیم از سمت یک کاربر به محصولات مورد علاقه اش دسترسی پیدا کنیم می توانیم از related_name استفاده کنیم.
hossein = User.objects.get(username='hossein')
hossein_favorite_products = hossein.favorite_products.all()
برای دسترسی به کاربرانی که یک محصول را پسندیده اند نیز می توان به صورت زیر عمل کرد:
rtx_4090 = Product.objects.get(slug='RTX-4090')
users_likes_rtx_4090 = rtx_4090.user_favorites.all()
فرض کنید که کاربر ما لاگین است و pk محصول مورد نظر را در اختیار داریم.
product = get_object_or_404(Product, id=product_id)
برای اضافه کردن محصول به علاقه مندی های کاربر از متد add و برای حذف آن از متد remove استفاده می کنیم.
request.user.favorite_products.add(product)
اگر این محصول از قبل در علاقه مندی های کاربر موجود بوده باشد، با add کردن مجدد خطایی رخ نمی دهد و رابطه ی تکراری اضافه نمی شود. اما چنانچه بخواهیم می توانیم بررسی کنیم که اگر محصول از قبل در علاقه مندی ها موجود بود، پیام خطای مناسب را به کاربر نمایش دهیم.
if not request.user.favorite_products.filter(id=product_id).exists():
request.user.favorite_products.add(product)
else:
messages.error(request, 'this product is already in the favorites.')
request.user.favorite_products.remove(product)
اگر تلاش به حذف محصولی از علاقه مندی های کاربر بکنیم که در علاقه مندی های کاربر موجود نباشد، خطایی دریافت نمی کنیم و هیچ اتفاقی نمی افتد. اما می توانید در این صورت به کاربر پیام خطای مناسب نمایش دهید.
هدف این پست صرفا آشنایی با کارکرد پایه ManyToManyField در جنگو بوده است. برای جزئیات بیشتر می توانید به مستندات جنگو مراجعه کنید.
در حال بارگذاری نظرات...

تاریخ انتشار
تعداد نظرات
زمان خواندن
دورههایی پروژهمحور، بهروز و با پشتیبانی کامل. از صفر تا استخدام.
در حال بارگذاری دورهها...
دورههایی پروژهمحور، بهروز و با پشتیبانی کامل. از صفر تا استخدام.
در حال بارگذاری دورهها...