رابطهی OneToOne
در مدلهای رابطهای، رابطه یک به یک (One-to-One) زمانی استفاده میشود که هر رکورد از جدول A دقیقا با یک رکورد از جدول B مرتبط باشد و برعکس. این نوع رابطه در جنگو با استفاده از فیلد OneToOneField پیادهسازی میشود.
یکی از رایحترین متداولترین سناریوهای استفاده از OneToOneField در پروژههای جنگو، ایجاد یک مدل پروفایل کاربری (Profile) برای گسترش مدل کاربر و ذخیره اطلاعات اضافی است. این کار زمانی ضروری میشود که بخواهیم از مدل User پیشفرض جنگو استفاده کنیم (بدون سفارشیسازی آن)، اما نیاز به فیلدهای بیشتری مانند تاریخ تولد، شماره تلفن، بیوگرافی یا تصویر پروفایل داشته باشیم. استفاده از OneToOneField در این مورد کاملا منطقی خواهد بود چرا که هر کاربر فقط یک پروفایل دارد و هر پروفایل فقط به یک کاربر تعلق دارد.
رابطه یک به یک در جنگو، یک راهحل تمیز و ایمن برای گسترش مدلهای موجود (بهویژه مدل User) بدون تغییر ساختار اصلی آنهاست. با استفاده از OneToOneField، میتوان بهراحتی اطلاعات جانبی را در مدل جداگانهای ذخیره کرده و همچنان از تمام قابلیتهای ORM جنگو (مانند دسترسی معکوس و فیلتر پیشرفته) بهره برد.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) # OneToOne Field
✺✳ دسترسی مستقیم و معکوس (Forward & Reverse Access) ✳✺
در OneToOneField، تفاوت منطقی بین forward و reverse محو میشود چرا که هر دو سمت یک شیء واحد را برمیگردانند. پس نیازی به تفکیک خاصی نیست و از دید کاربر، انگار دو طرف رابطه همسطح هستند.
💡 در رابطه یکبهیک، رابطه معکوس همیشه یک رکورد خواهد بود و نه لیستی از رکوردها ( QuerySet ) پس نیازی به _set و حتی .all() نیست (چون چندتا نیست!). و نام آن همان نام مدل کوچکشده است ( همانند profile، برای مدل Profile ).
⮜ دسترسی مستقیم از سمت هر دو مدل - Forward
— دسترسی به پروفایل از طریق کاربر
user = User.objects.get(username="admin")
profile = user.profile
DoesNotExist مواجه میشود. برای جلوگیری، میتوان از hasattr یا get_or_create استفاده نمود.— دسترسی به کاربر از طریق پروفایل
profile = Profile.objects.get(id=1)
user = profile.user
⚠️ این دسترسی همیشه ممکن است (چون هر پروفایل حتما به یک کاربر مرتبط است).
✺✳ فیلتر کردن بر اساس روابط (Lookups Expression) ✳✺
یادآور میشویم که در رابطههای OneToOne نیز جنگو همچنان اجازه میدهد تا با استفاده از lookupهای معکوس (__)، بر اساس فیلدهای مدلهای مرتبط جستجو کنیم.
— دریافت کاربرانی که قبل از سال ۲۰۰۰ متولد شدهاند.
users = User.objects.filter(profile__birth__lte="2000-01-01")
⚠️ اگر related_name را در OneToOneField تغییر داده باشیم (مثلا به user_profile)، باید از همان نام در lookup استفاده کنیم
— دریافت تمام پروفایلهایی که کاربریشان بعد از تاریخ خاصی ایجاد شدهاند.
profiles = Profile.objects.filter(user__date_joined__gte="2023-01-01")