رابطه چند به چند - ManyToMany


در رابطه چند به چند (Many-to-Many یا به اختصار ManyToMany)، هر رکورد از یک مدل می‌تواند با چندین رکورد از مدل دیگر مرتبط شود و بالعکس. این نوع رابطه زمانی کاربرد دارد که بین داده‌های دو مدل، ارتباطِ یک‌طرفه FoerignKey یا یک‌به‌یک OneToOne کافی نباشد. فرض کنیم در یک وب‌سایت پورتفولیوی شخصی برای نمایش لیستی از تجربه پروژه‌های نرم‌افزاری اجراشده، هر پروژه می‌تواند چندین تگ (برچسب) داشته باشد — Python، Django، React و غیره. از سوی دیگر، یک تگ مانند Python می‌تواند به چندین پروژه مختلف تعلق داشته باشد (به عنوان مثال،یک پروژه وب، یک پروژه اسکریپتی و یا یک ربات تلگرام که همگی با پایتون اجرا شده‌ باشند). در چنین شرایطی، رابطه ManyToMany ایده‌آل‌ترین گزینه خواهد بود.

 

Many2Many-Field

 

در سطح پایگاه داده، رابطه چند به چند به‌صورت مستقیم قابل پیاده‌سازی نیست. به همین دلیل، جنگو به‌صورت خودکار یک جدول میانی (Intermediate Table یا Junction Table) ایجاد می‌کند. این جدول دو ستون دارد که هر ستون به id  رکورد یا فیلد کلید اصلی (Primary Key) در مدل مقابل اشاره می‌کند. هر بار که یک تگ به یک پروژه اضافه گردد، جنگو یک رکورد جدید در جدول میانی درج می‌کند که شامل project_id و tag_id خواهد بود.

TABLE 🡺 coreapp_project_tags

  id  project_id                        tag_id
----  --------------------------------  --------------------------------
   1  ac260bde5f1347449f239052420573a7  ab2050e16c94483db24aeab26b0c4330
   2  0fac050182a84fffa37d6033c276d2c2  ab2050e16c94483db24aeab26b0c4330
   3  45da0e0c546a4b0991f665edba168b89  ab2050e16c94483db24aeab26b0c4330
   4  ac260bde5f1347449f239052420573a7  41de4854a25b40ac860c5b28ff4b8c17
   5  ac260bde5f1347449f239052420573a7  50cb432fb44c487daedfd427eac08bcd

 

برای ایجاد یک رابطه چند به چند در مدل‌های جنگو، از فیلد ManyToManyField استفاده می‌گردد. فیلد ManyToManyField فقط در یکی از دو مدل تعریف می‌گردد— به طور معمول، در مدلی که از لحاظ منطقی "مالک" رابطه تلقی می‌گردد. اگر ManyToManyField را در هر دو مدل قرار دهیم، جنگو دو جدول میانی جداگانه ایجاد می‌کند که کاملاً اشتباه است! رابطه چند به چند یک رابطه دوطرفه است و فقط نیاز به یک فیلد در یکی از مدل‌ها دارد.

coreapp/models.py

# coreapp.models.py
from django.db import models
from django.contrib.auth.models import User
import uuid

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return self.name

#-------------------------------------------------------------------------------------------

class Project(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="projects")
    title = models.CharField(max_length=100)
    area = models.CharField(max_length=50)
    content = models.TextField(null=True, blank=True)
    tags = models.ManyToManyField(Tag, related_name='projects')
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return self.title

⚠️ چون پروژه‌ها مالک تگ‌ها محسوب می‌شوند، فیلد ManyToMany در مدل project تعریف شده است.

⚠️ برای بازتاب تغییرات جدید در ساختار پایگاه‌داده، الزامی است که دو دستور makemigrations و migrate به ترتیب اجرا شوند.

⚠️ ثبت مدل Tag ایجاد شده، در admins.py جهت نمایش در پنل ادمین، توسط دستور admin.site.register(models.Tag) می‌بایست صورت پذیرد.