رابطه یک به چند - 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")  # ManyToOne Field
    title = models.CharField(max_length=100)
    area = models.CharField(max_length=50)
    content = models.TextField(null=True, blank=True)
    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

در مدل 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 به ترتیب اجرا شوند

با بررسی داده‌های واقعی در جداول، این رابطه به‌وضوح قابل مشاهده است. در جدول auth_user، کاربری با id = 1 و نام کاربری admin وجود دارد که پیشتر در بخش ایجاد کاربری superuser ایجاد گردید.

TABLE 🡺 auth_user

id    username    email            first_name  last_name   is_superuser     is_staff    is_active  
----  ----------  ---------------  ----------- ---------   --------------   ----------  -----------
1  	  admin       admin@admin.com              			   1  		        1           1  

در مقابل، در جدول coreapp_project، سه رکورد مختلف وجود دارد که همگی در فیلد owner_id مقدار 1 دارند — یعنی هر سه پروژه Fleunt Speech و Coverage Map و Yoga Academy به همان کاربر admin تعلق دارند..

TABLE 🡺 coreapp_project

owner_id	title          area      content    created         updated         id     
----------  -------------  --------  ---------  --------------  --------------  -------
1			Fluent Speech  Health               2025-09-29 ...  2025-09-29 ...  ac26...
1			Coeverage Map  Industry             2025-09-29 ...  2025-09-29 ...  0fac...
1			Yoga Academy   Sport                2025-09-29 ...  2025-09-29 ...  45da...

 

این دقیقاً همان مفهوم One-to-Many است که در این نمونه با مفهوم یک کاربر ←→ چندین پروژه، از دید پایگاه‌ داده می‌باشد، این رابطه با یک کلید خارجی (Foreign Key) پیاده‌سازی شده که تمامیت رابطه را تضمین می‌کند — یعنی هیچ پروژه‌ای نمی‌تواند owner_idای داشته باشد که در جدول auth_user وجود نداشته باشد (مگر در صورتی که on_delete مقدار SET_NULL یا SET_DEFAULT داشته باشد — بخش مربوط به پارامترهای فیلد — که در این مدل پیاده‌سازی نشده است).

این تحلیل نشان می‌دهد که رابطه تعریف‌شده در سطح مدل، نه‌تنها در کد بلکه در ساختار فیزیکی پایگاه‌داده نیز به‌درستی پیاده‌سازی شده و با داده‌های واقعی همخوانی کامل دارد.