رابطه‌ی ForeignKey


گفتیم که در مدل‌های رابطه‌ای داده‌ها، یکی از رایج‌ترین انواع روابط، رابطه یک به چند (One-to-Many) که در جنگو با ForeignKeyField تعریف می‌گردد. همجنین مدلی برای مدیریت پروژه‌ها ایجاد کردیم که هر پروژه از مدل فرزند Project به یک کاربر (مالک) از مدل والد User تعلق داشته باشد. ولی در عین حال، کاربران می‌توانستند مالک چندین پروژه باشند.

class Project(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="projects")  # ManyToOne Field
    ...

✺✳ دسترسی مستقیم و معکوس (Direct & Reverse Access) ✳✺ 

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

 

از سمت فرزند به والد (Forward)


— دریافت مالک یک پروژه

projectObj = Project.objects.get(id="ac260bde5f1347449f239052420573a7")
owner = projectObj.owner

⚠️ در اینجا، owner نام فیلدی است که در مدل پروژه (Project) تعریف شده و یک رابطه یک‌به‌چند (OneToMany) یا همان ForeignKey را با مدل User ایجاد کرده است.

 

⮜ از سمت والد به فرزندان (Reverse)


— دریافت تمام پروژه‌های یک کاربر

owner = User.objects.get(id=1) 

# Method 1: If related_name is not set (Django default)
projects = owner.project_set.all()

# Method 2: When related_name is set to "projects" (recommended)
projects = owner.projects.all() 

⚠️ اگر در تعریف ForeignKey از پارامتر related_name استفاده نموده باشیم، نباید از نام پیش‌فرض (modelname_set) استفاده کنیم. در غیر این صورت با خطای AttributeError مواجه خواهیم شد.

 

✺✳ فیلتر کردن بر اساس روابط (Lookups Expressions) ✳✺ 

یکی از قابلیت‌های قوی ORM جنگو، lookup expressions است که اجازه می‌دهد تا با استفاده از دو زیرخط  (__)، داده‌ها را بر اساس فیلدهای مدل‌های مرتبط جستجو نمود.

 

از سمت فرزند به والد (Forward)


— دریافت تمام پروژه‌های یک کاربر

projectObjs = Project.objects.filter(owner__username="admin")

⚠️ در اینجا، owner نام فیلدی است که در مدل پروژه (Project) تعریف شده و یک رابطه یک‌به‌چند (OneToMany) یا همان ForeignKey را با مدل User ایجاد کرده است.

 

از سمت والد به فرزندان (Reverse)


— دریافت تمام کاربرانی که حداقل یک پروژه در حوزه "ai" دارند

# Method 1: If related_name is not set (Django default)
userObjs = User.objects.filter(project__area__icontains="ai")

# Method 2: When related_name is set to "projects" (recommended)
users = User.objects.filter(projects__area__icontains="ai")

⚠️ project نام مدل مرتبط است ( در صورت عدم تعریف پارامتر related_name، جنگو به‌صورت پیش‌فرض، از نام مدل به صورت کوچک‌شده استفاده می‌کند). اما اگر related_name="projects" تعریف شده باشد، باید از آن استفاده کرد.