~4 دقیقه مطالعه • بروزرسانی ۲۴ اسفند ۱۴۰۴
مقدمه
در جنگو، هر مدل یک کلید اصلی (Primary Key) دارد که بهطور پیشفرض یک فیلد تکی است. اما در برخی طراحیهای پایگاهداده، لازم است کلید اصلی از چند فیلد تشکیل شود. از Django 5.2 به بعد، جنگو از کلیدهای اصلی مرکب پشتیبانی میکند.
تعریف Composite Primary Key
برای تعریف کلید مرکب، کافی است در مدل، ویژگی pk را برابر CompositePrimaryKey قرار دهید:
class OrderLineItem(models.Model):
pk = models.CompositePrimaryKey("product_id", "order_id")
product = models.ForeignKey(Product, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
quantity = models.IntegerField()
این کار باعث میشود جنگو در دیتابیس کلید اصلی مرکب ایجاد کند:
PRIMARY KEY (product_id, order_id)
ساختار pk در کلیدهای مرکب
در مدلهایی با کلید مرکب، مقدار pk یک tuple است:
item.pk
# (1, "A755H")
میتوانید pk را مستقیماً مقداردهی کنید:
item = OrderLineItem(pk=(2, "B142C"))
item.product_id # 2
item.order_id # "B142C"
فیلتر کردن نیز با tuple انجام میشود:
OrderLineItem.objects.filter(pk=(1, "A755H")).count()
محدودیتها و وضعیت توسعه
پشتیبانی از کلیدهای مرکب هنوز کامل نیست. در حال حاضر موارد زیر پشتیبانی نمیشوند:
- ForeignKey به مدل دارای کلید مرکب
- GenericForeignKey
- ثبت مدل در Django Admin
این قابلیتها در نسخههای آینده اضافه خواهند شد.
مهاجرت به کلید مرکب
جنگو نمیتواند یک جدول موجود را از کلید تکی به کلید مرکب مهاجرت دهد. همچنین نمیتواند فیلدی را به کلید مرکب اضافه یا حذف کند.
برای مهاجرت:
- ابتدا در دیتابیس، کلید مرکب را بهصورت دستی ایجاد کنید.
- سپس در مدل، CompositePrimaryKey را اضافه کنید.
- مهاجرتهای مربوطه را با --fake اعمال کنید.
- یا از SeparateDatabaseAndState استفاده کنید.
کلیدهای مرکب و روابط
ForeignKey نمیتواند به مدل دارای کلید مرکب اشاره کند:
class Foo(models.Model):
item = models.ForeignKey(OrderLineItem) # ❌ پشتیبانی نمیشود
راهحل: ForeignObject
میتوانید از ForeignObject استفاده کنید:
class Foo(models.Model):
item_order_id = models.CharField(max_length=20)
item_product_id = models.IntegerField()
item = models.ForeignObject(
OrderLineItem,
on_delete=models.CASCADE,
from_fields=("item_order_id", "item_product_id"),
to_fields=("order_id", "product_id"),
)
توجه: ForeignObject یک API داخلی است و شامل محدودیتها و ریسکهای خودش است.
کلیدهای مرکب و توابع پایگاهداده
بسیاری از توابع پایگاهداده فقط یک ستون را میپذیرند:
Max("order_id") # OK
Max("product_id") # OK
Max("pk") # ❌ ValueError
Count("pk") # OK
زیرا pk شامل چند ستون است.
کلیدهای مرکب در فرمها
چون pk یک فیلد واقعی نیست، در ModelForm ظاهر نمیشود:
اضافه کردن pk به فرم باعث FieldError میشود.
همچنین تغییر مقدار pk یک شیء موجود باعث ایجاد یک شیء جدید میشود. بنابراین بهتر است فیلدهای کلید اصلی را editable=False کنید.
اعتبارسنجی مدل با کلید مرکب
چون pk یک فیلد مجازی است، exclude={"pk"} در clean_fields() اثری ندارد. برای حذف از اعتبارسنجی، باید هر فیلد را جداگانه exclude کنید.
اما validate_unique() میتواند exclude={"pk"} داشته باشد.
تشخیص کلیدهای مرکب در کد
قبلاً میشد با بررسی field.primary_key کلید اصلی را پیدا کرد. اما در کلیدهای مرکب، هیچ فیلدی primary_key=True ندارد.
برای شناسایی کلیدهای مرکب از _meta.pk_fields استفاده کنید:
Product._meta.pk_fields
# []
OrderLineItem._meta.pk_fields
# [, ]
جمعبندی
کلیدهای اصلی مرکب در Django 5.2 یک قابلیت مهم برای پروژههایی هستند که نیاز به کلیدهای چندستونه دارند. با وجود برخی محدودیتها در روابط، فرمها و مهاجرتها، این ویژگی اکنون بهصورت رسمی پشتیبانی میشود و امکان طراحی دیتابیسهای پیچیدهتر را فراهم میکند.
نوشته و پژوهش شده توسط دکتر شاهین صیامی