رابطه یک به چند - ForeignKey


در سیستم‌های مدیریت پایگاه‌داده‌های رابطه‌ای و به‌ویژه در چارچوب جنگو، رابطه ForeignKey ابزاری بنیادین برای ایجاد ارتباط جهت‌دار بین دو مدل محسوب می‌شود که از نظر منطقی معادل یک رابطهٔ یک به چند (One-to-Many) است. این بدان معناست که یک رکورد در مدل والد — مثلاً User — می‌تواند با چندین رکورد در مدل فرزند — مثلاً Project — مرتبط باشد، در حالی که هر رکورد در مدل فرزند تنها می‌تواند به یک رکورد منحصربه‌فرد در مدل والد ارجاع دهد. این الگو در مثال‌های واقعی مانند «مسئولیت یک کاربر بر چندین پروژه، در حالی که هر پروژه تنها یک مسئول دارد» به خوبی قابل مشاهده است. • از دید مدل فرزند (Project)، همین رابطه به‌عنوان چند به یک (ManyToOne) تفسیر می‌شود، زیرا چندین رکورد در این مدل به یک رکورد واحد در مدل والد اشاره می‌کنند.

این دوگانگی در نام‌گذاری رابطه — OneToMany از دید والد و ManyToOne از دید فرزند — گاهی برای توسعه‌دهندگان مبتدی گیج‌کننده است، اما درک دقیق آن شرط لازم برای طراحی صحیح مدل‌ها و دسترسی کارآمد به داده‌های مرتبط در لایه منطق کسب‌وکار است.

در پیاده‌سازی، فیلد ForeignKey همواره در مدل فرزند تعریف می‌گردد و به مدل والد متصل می‌شود — چرا که در سطح پایگاه‌داده، این مدل فرزند است که نیازمند ارجاع به اطلاعات مدل والد خواهد بود. در واقع، فرآیندی که در سطح پایگاه‌داده اتفاق می‌افتد به این صورت است که هنگام تعریف یک فیلد ForeignKey در مدل فرزند، یک ستون جدید (مثلاً owner_id) به صورت خودکار در جدول مربوط به مدل فرزند اضافه می‌شود که به کلید اصلی مدل والد (User) اشاره می‌کند.

coreapp/models.py

from django.db import models
from django.contrib.auth.models import User
import uuid

class project(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="projects")
    title = models.CharField(max_length=200)
    subject = models.CharField(max_length=500)
    content = models.TextField(null=True, blank=True)
    demo = models.URLField(null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)

    def __str__(self):
        return self.title

در مدل Project که در کد فوق تعریف شده است، رابطهٔ ForeignKey از طریق فیلد owner به مدل User (از اپ django.contrib.auth) متصل شده است که مدل پیش‌فرض جنگو بوده و هنگام ایجاد کاربری superuser شرح مختصری از آن رفت. این تعریف به‌صورت دقیق یک رابطه چند به یک (ManyToOne) را از دید مدل Project پیاده‌سازی می‌کند — یعنی هر پروژه تنها می‌تواند یک مالک (owner) داشته باشد، اما یک کاربر می‌تواند مالک چندین پروژه باشد. این رابطه در سطح پایگاه‌داده با افزودن یک ستون owner_id در جدول project پیاده‌سازی می‌شود که به کلید اصلی (id) جدول auth_user اشاره می‌کند.

⚠️ پارامتر on_delete=models.CASCADE تعیین می‌کند که در صورت حذف کاربر، تمام پروژه‌های مرتبط با او نیز به‌صورت خودکار حذف خواهند شد — رفتاری که در بسیاری از سیستم‌ها برای حفظ تمامیت داده‌ها منطقی و مطلوب است. 

⚠️ در جنگو، برای دسترسی معکوس — یعنی از مدل والد به مدل فرزند — از ویژگی related_name استفاده می‌شود که در زمان تعریف ForeignKey اختیاری اما بسیار توصیه‌شده است. این ویژگی امکان تعریف یک نام معنادار برای دسترسی به مجموعهٔ رکوردهای مرتبط را فراهم می‌کند

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