راهنمای جامع سریالسازی اشیاء تودرتو در Django REST Framework (DRF) با استفاده از سریالایزرها، شامل انواع روابط و تکنیکهای پیشرفته.
روابط سریالایزر در DRF پایتون: تسلط بر سریالسازی اشیاء تودرتو
فریمورک Django REST (DRF) یک سیستم قدرتمند و انعطافپذیر برای ساخت APIهای وب فراهم میکند. یک جنبه حیاتی در توسعه API، مدیریت روابط بین مدلهای داده است و سریالایزرهای DRF مکانیزمهای قدرتمندی برای سریالسازی و دیسریالسازی اشیاء تودرتو ارائه میدهند. این راهنما روشهای مختلف مدیریت روابط در سریالایزرهای DRF را با ارائه مثالهای عملی و بهترین شیوهها بررسی میکند.
درک روابط سریالایزر
در پایگاههای داده رابطهای، روابط نحوه اتصال جداول یا مدلهای مختلف را تعریف میکنند. سریالایزرهای DRF باید این روابط را هنگام تبدیل اشیاء پایگاه داده به JSON یا فرمتهای داده دیگر برای مصرف API منعکس کنند. ما سه نوع اصلی روابط را پوشش خواهیم داد:
- ForeignKey (یک-به-چند): یک شیء با چندین شیء دیگر در ارتباط است. به عنوان مثال، یک نویسنده میتواند کتابهای زیادی بنویسد.
- ManyToManyField (چند-به-چند): چندین شیء با چندین شیء دیگر در ارتباط هستند. به عنوان مثال، چندین نویسنده میتوانند روی چندین کتاب همکاری کنند.
- OneToOneField (یک-به-یک): یک شیء به طور منحصربهفرد با یک شیء دیگر در ارتباط است. به عنوان مثال، یک پروفایل کاربری اغلب به صورت یک-به-یک با یک حساب کاربری مرتبط است.
سریالسازی تودرتوی پایه با ForeignKey
بیایید با یک مثال ساده از سریالسازی یک رابطه ForeignKey شروع کنیم. این مدلها را در نظر بگیرید:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
country = models.CharField(max_length=50, default='USA') # Adding country field for international context
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
publication_date = models.DateField()
def __str__(self):
return self.title
برای سریالسازی مدل `Book` به همراه دادههای مربوط به `Author`، میتوانیم از یک سریالایزر تودرتو استفاده کنیم:
from rest_framework import serializers
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name', 'country']
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True) # Changed from PrimaryKeyRelatedField
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
در این مثال، `BookSerializer` شامل یک فیلد `AuthorSerializer` است. `read_only=True` فیلد `author` را فقط خواندنی میکند و از تغییر نویسنده از طریق اندپوینت کتاب جلوگیری میکند. اگر نیاز به ایجاد یا بهروزرسانی کتابها با اطلاعات نویسنده دارید، باید عملیات نوشتن را به طور متفاوتی مدیریت کنید (به بخش زیر مراجعه کنید).
اکنون، هنگامی که یک شیء `Book` را سریالسازی میکنید، خروجی JSON شامل جزئیات کامل نویسنده به صورت تودرتو در دادههای کتاب خواهد بود:
{
"id": 1,
"title": "The Hitchhiker's Guide to the Galaxy",
"author": {
"id": 1,
"name": "Douglas Adams",
"country": "UK"
},
"publication_date": "1979-10-12"
}
سریالسازی روابط ManyToManyField
بیایید یک رابطه `ManyToManyField` را در نظر بگیریم. فرض کنید یک مدل `Category` داریم و یک کتاب میتواند به چندین دستهبندی تعلق داشته باشد.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
categories = models.ManyToManyField(Category, related_name='books')
publication_date = models.DateField()
def __str__(self):
return self.title
ما میتوانیم دستهبندیها را با استفاده از `serializers.StringRelatedField` یا `serializers.PrimaryKeyRelatedField` سریالسازی کنیم، یا یک سریالایزر تودرتو ایجاد کنیم.
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
categories = CategorySerializer(many=True, read_only=True) # many=True is essential for ManyToManyField
class Meta:
model = Book
fields = ['id', 'title', 'author', 'categories', 'publication_date']
آرگومان `many=True` هنگام سریالسازی یک `ManyToManyField` بسیار مهم است. این به سریالایزر میگوید که انتظار یک لیست از اشیاء دستهبندی را داشته باشد. خروجی به این شکل خواهد بود:
{
"id": 1,
"title": "Pride and Prejudice",
"author": {
"id": 2,
"name": "Jane Austen",
"country": "UK"
},
"categories": [
{
"id": 1,
"name": "Classic Literature"
},
{
"id": 2,
"name": "Romance"
}
],
"publication_date": "1813-01-28"
}
سریالسازی روابط OneToOneField
برای روابط `OneToOneField`، رویکرد مشابه ForeignKey است، اما مهم است که مواردی را که شیء مرتبط ممکن است وجود نداشته باشد، مدیریت کنید.
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField(blank=True)
location = models.CharField(max_length=100, blank=True, default='Global') # Added location for international context
def __str__(self):
return self.user.username
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['id', 'bio', 'location']
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'profile']
خروجی به این صورت خواهد بود:
{
"id": 1,
"username": "johndoe",
"email": "john.doe@example.com",
"profile": {
"id": 1,
"bio": "Software Engineer.",
"location": "London, UK"
}
}
مدیریت عملیات نوشتن (ایجاد و بهروزرسانی)
مثالهای بالا عمدتاً بر روی سریالسازی فقط-خواندنی تمرکز دارند. برای اینکه امکان ایجاد یا بهروزرسانی اشیاء مرتبط فراهم شود، باید متدهای `create()` و `update()` را در سریالایزر خود بازنویسی (override) کنید.
ایجاد اشیاء تودرتو
فرض کنید میخواهید یک کتاب و نویسنده جدید را به طور همزمان ایجاد کنید.
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
def create(self, validated_data):
author_data = validated_data.pop('author')
author = Author.objects.create(**author_data)
book = Book.objects.create(author=author, **validated_data)
return book
در متد `create()`، ما دادههای نویسنده را استخراج میکنیم، یک شیء `Author` جدید ایجاد میکنیم و سپس شیء `Book` را با مرتبط کردن آن با نویسنده تازه ایجاد شده، میسازیم.
مهم: شما باید خطاهای اعتبارسنجی احتمالی در `author_data` را مدیریت کنید. میتوانید از یک بلاک try-except استفاده کرده و در صورت نامعتبر بودن دادههای نویسنده، یک `serializers.ValidationError` ایجاد کنید.
بهروزرسانی اشیاء تودرتو
به همین ترتیب، برای بهروزرسانی همزمان یک کتاب و نویسندهاش:
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
def update(self, instance, validated_data):
author_data = validated_data.pop('author', None)
if author_data:
author = instance.author
for attr, value in author_data.items():
setattr(author, attr, value)
author.save()
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
در متد `update()`، ما نویسنده موجود را بازیابی میکنیم، ویژگیهای آن را بر اساس دادههای ارائه شده بهروز میکنیم و سپس ویژگیهای کتاب را بهروز میکنیم. اگر `author_data` ارائه نشود (یعنی نویسنده در حال بهروزرسانی نیست)، کد بخش بهروزرسانی نویسنده را رد میکند. مقدار پیشفرض `None` در `validated_data.pop('author', None)` برای مدیریت مواردی که دادههای نویسنده در درخواست بهروزرسانی وجود ندارد، حیاتی است.
استفاده از PrimaryKeyRelatedField
به جای سریالایزرهای تودرتو، میتوانید از `PrimaryKeyRelatedField` برای نمایش روابط با استفاده از کلید اصلی شیء مرتبط استفاده کنید. این زمانی مفید است که فقط نیاز به ارجاع به شناسه شیء مرتبط دارید و نمیخواهید کل شیء را سریالسازی کنید.
class BookSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all())
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
اکنون، فیلد `author` شامل شناسه (ID) نویسنده خواهد بود:
{
"id": 1,
"title": "1984",
"author": 3, // Author ID
"publication_date": "1949-06-08"
}
برای ایجاد و بهروزرسانی، شما باید شناسه نویسنده را در دادههای درخواست ارسال کنید. `queryset=Author.objects.all()` تضمین میکند که شناسه ارائه شده در پایگاه داده وجود دارد.
استفاده از HyperlinkedRelatedField
`HyperlinkedRelatedField` روابط را با استفاده از هایپرلینکها به اندپوینت API شیء مرتبط نمایش میدهد. این امر در APIهای هایپرمیدیا (HATEOAS) رایج است.
class BookSerializer(serializers.ModelSerializer):
author = serializers.HyperlinkedRelatedField(view_name='author-detail', read_only=True)
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publication_date']
آرگومان `view_name` نام ویویی را مشخص میکند که درخواستهای مربوط به شیء مرتبط را مدیریت میکند (مثلاً `author-detail`). شما باید این ویو را در فایل `urls.py` خود تعریف کنید.
خروجی شامل یک URL خواهد بود که به اندپوینت جزئیات نویسنده اشاره میکند:
{
"id": 1,
"title": "Brave New World",
"author": "http://example.com/api/authors/4/",
"publication_date": "1932-01-01"
}
تکنیکها و ملاحظات پیشرفته
- گزینه
depth
: درModelSerializer
، میتوانید از گزینهdepth
برای ایجاد خودکار سریالایزرهای تودرتو برای روابط ForeignKey تا عمق مشخصی استفاده کنید. با این حال، استفاده ازdepth
میتواند منجر به مشکلات عملکردی در روابط پیچیده شود، بنابراین به طور کلی توصیه میشود که سریالایزرها را به صراحت تعریف کنید. SerializerMethodField
: ازSerializerMethodField
برای ایجاد منطق سریالسازی سفارشی برای دادههای مرتبط استفاده کنید. این زمانی مفید است که نیاز دارید دادهها را به شکل خاصی قالببندی کنید یا مقادیر محاسبهشده را شامل شوید. به عنوان مثال، میتوانید نام کامل نویسنده را بر اساس منطقه (locale) با ترتیبهای مختلف نمایش دهید. در بسیاری از فرهنگهای آسیایی، نام خانوادگی قبل از نام کوچک میآید.- سفارشیسازی نمایش: متد
to_representation()
را در سریالایزر خود بازنویسی کنید تا نحوه نمایش دادههای مرتبط را سفارشی کنید. - بهینهسازی عملکرد: برای روابط پیچیده و مجموعهدادههای بزرگ، از تکنیکهایی مانند select_related و prefetch_related برای بهینهسازی کوئریهای پایگاه داده و کاهش تعداد تماسها با پایگاه داده استفاده کنید. این امر به ویژه برای APIهایی که به کاربران جهانی با اتصالات کندتر سرویس میدهند، مهم است.
- مدیریت مقادیر Null: مراقب نحوه مدیریت مقادیر null در سریالایزرهای خود باشید، به خصوص هنگام کار با روابط اختیاری. در صورت لزوم از
allow_null=True
در فیلدهای سریالایزر خود استفاده کنید. - اعتبارسنجی: اعتبارسنجی قوی برای اطمینان از یکپارچگی دادهها، به ویژه هنگام ایجاد یا بهروزرسانی اشیاء مرتبط، پیادهسازی کنید. برای اجرای قوانین کسبوکار از اعتبارسنجهای سفارشی استفاده کنید. به عنوان مثال، تاریخ انتشار یک کتاب نباید در آینده باشد.
- بینالمللیسازی و محلیسازی (i18n/l10n): در نظر بگیرید که دادههای شما چگونه در زبانها و مناطق مختلف نمایش داده میشوند. تاریخها، اعداد و ارزها را متناسب با منطقه کاربر قالببندی کنید. رشتههای قابل بینالمللیسازی را در مدلها و سریالایزرهای خود ذخیره کنید.
بهترین شیوهها برای روابط سریالایزر
- سریالایزرها را متمرکز نگه دارید: هر سریالایزر باید مسئول سریالسازی یک مدل خاص یا مجموعهای از دادههای نزدیک به هم باشد. از ایجاد سریالایزرهای بیش از حد پیچیده خودداری کنید.
- از سریالایزرهای صریح استفاده کنید: از اتکای بیش از حد به گزینه
depth
خودداری کنید. برای هر مدل مرتبط، سریالایزرهای صریح تعریف کنید تا کنترل بیشتری بر فرآیند سریالسازی داشته باشید. - به طور کامل تست کنید: تستهای واحد بنویسید تا تأیید کنید که سریالایزرهای شما دادهها را به درستی سریالسازی و دیسریالسازی میکنند، به ویژه هنگام کار با روابط پیچیده.
- API خود را مستند کنید: اندپوینتهای API و فرمتهای دادهای که انتظار دارند و برمیگردانند را به وضوح مستند کنید. از ابزارهایی مانند Swagger یا OpenAPI برای تولید مستندات API تعاملی استفاده کنید.
- نسخهبندی API را در نظر بگیرید: با تکامل API خود، از نسخهبندی برای حفظ سازگاری با کلاینتهای موجود استفاده کنید. این به شما امکان میدهد تغییرات شکننده را بدون تأثیر بر برنامههای قدیمیتر معرفی کنید.
- عملکرد را نظارت کنید: عملکرد API خود را نظارت کرده و هرگونه گلوگاه مربوط به روابط سریالایزر را شناسایی کنید. از ابزارهای پروفایلینگ برای بهینهسازی کوئریهای پایگاه داده و منطق سریالسازی استفاده کنید.
نتیجهگیری
تسلط بر روابط سریالایزر در Django REST Framework برای ساخت APIهای وب قوی و کارآمد ضروری است. با درک انواع مختلف روابط و گزینههای متنوع موجود در سریالایزرهای DRF، میتوانید به طور موثر اشیاء تودرتو را سریالسازی و دیسریالسازی کنید، عملیات نوشتن را مدیریت کرده و API خود را برای عملکرد بهینه کنید. به یاد داشته باشید که هنگام طراحی API خود، بینالمللیسازی و محلیسازی را در نظر بگیرید تا اطمینان حاصل شود که برای مخاطبان جهانی قابل دسترسی است. تست کامل و مستندات واضح کلید تضمین قابلیت نگهداری و استفادهپذیری طولانیمدت API شما هستند.