تا اینجا یاد گرفتید چطور یک ویو ساده با @api_view بنویسید. در آن ویوها، داده را دستی به دیکشنری تبدیل می‌کردیم. برای هر بار تبدیل، کد جداگانه می‌نوشتیم.

این روش برای پروژه‌های کوچک شاید کار کند. اما وقتی مدل شما ۲۰ فیلد داشته باشد، نوشتن این کد تبدیل برای هر مدل تکراری و خسته‌کننده می‌شود.

اینجا جایی است که سریالایزرها وارد می‌شوند.

سریالایزر در DRF کاری شبیه همان فرم‌ها در جنگوی معمولی انجام می‌دهد. داده مدل را می‌گیرد و به JSON تبدیل می‌کند. JSON دریافتی از کاربر را می‌گیرد، اعتبارسنجی می‌کند، و به مدل تبدیل می‌نماید.

به همین دلیل به آن قلب DRF می‌گویند. بدون سریالایزر، هیچکدام از ویوهای پیشرفته‌تر (کلاس‌بیس، جنریک، ویوست) کار نمی‌کنند.

در این درس از صفر شروع می‌کنیم. اول با یک Serializer ساده کار می‌کنیم. بعد می‌بینیم ModelSerializer چطور کد ما را کوتاه‌تر می‌کند. در انتها یاد می‌گیرید چطور اعتبارسنجی سفارشی بنویسید و خروجی سریالایزر را تغییر دهید.

اگر درس‌های قبل را خوب متوجه شده‌اید، برای این درس کاملاً آماده هستید. تنها چیزی که نیاز دارید یک مدل ساده است که همان اول درس می‌سازیم.

Serializer چیست و چرا استفاده می‌شود؟

Serializer یک کلاس در DRF است. کار اصلی آن تبدیل داده‌ها از یک فرمت به فرمت دیگر است.

دو جهت برای این تبدیل وجود دارد.

جهت اول: از مدل به JSON

داده داخل دیتابیس شما به شکل مدل‌های جنگو است. اما کلاینت (مثل مرورگر یا اپلیکیشن موبایل) به JSON نیاز دارد. Serializer مدل را می‌گیرد و JSON می‌سازد.

جهت دوم: از JSON به مدل

کاربر یک درخواست POST می‌فرستد. داده داخل بدنه به شکل JSON است. Serializer این JSON را می‌گیرد، اعتبارسنجی می‌کند، و به داده‌ای تبدیل می‌کند که بتوانید در دیتابیس ذخیره کنید.

چرا بدون Serializer کار سخت می‌شود؟

فرض کنید مدل Article دارید با ۱۵ فیلد. برای تبدیل یک شیء از این مدل به JSON، باید بنویسید:

article_data = {
    'id': article.id,
    'title': article.title,
    'content': article.content,
    'author': article.author.username,
    'created_at': article.created_at,
    # و ۱۰ فیلد دیگر ...
}

حالا فرض کنید ۱۰۰ مقاله را می‌خواهید برگردانید. باید یک حلقه بزنید و برای هر مقاله این دیکشنری را بسازید.

همین کار را برای ورودی JSON هم باید انجام دهید. بیایید JSON کاربر را بگیرید، فیلدها را یکی یکی بررسی کنید، اعتبارسنجی دستی بنویسید، و بعد مدل بسازید.

این حجم کد تکراری، هم خسته‌کننده است و هم خطا دارد. فراموش می‌کنید فلان فیلد را اضافه کنید. یا اعتبارسنجی را درست ننوشته‌اید.

Serializer این مشکلات را حل می‌کند

با یک کلاس Serializer، شما مشخص می‌کنید چه فیلدهایی دارید. DRF بقیه کارها را انجام می‌دهد:

تبدیل خودکار مدل به JSON

تبدیل خودکار JSON به دیکشنری پایتون

اعتبارسنجی نوع داده (مثلاً عدد باشد یا رشته)

چک کردن فیلدهای اجباری

برگرداندن خطاهای استاندارد در صورت اشتباه بودن ورودی

یک مثال ساده:

from rest_framework import serializers

class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()

با این چند خط، شما یک Serializer دارید که می‌تواند:

یک شیء مقاله را به JSON تبدیل کند

JSON ورودی را اعتبارسنجی کند (مثلاً عنوان نباید بیشتر از ۲۰۰ کاراکتر باشد)

خطاهای استاندارد برگرداند

در بخش بعدی، این Serializer را به یک مدل واقعی متصل می‌کنیم و می‌بینیم چطور کار می‌کند.

ساخت Serializer برای مدل‌های Django

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

ساختن مدل نمونه

در اپلیکیشن myapi، فایل models.py را باز کنید. کد زیر را وارد کنید:

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.CharField(max_length=100)
    published_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title

این مدل چهار فیلد دارد. title حداکثر ۲۰۰ کاراکتر، content بدون محدودیت، author نام نویسنده، و published_at که خودکار زمان ایجاد را ثبت می‌کند.

بعد از نوشتن مدل، دو دستور را در ترمینال اجرا کنید:

python manage.py makemigrations
python manage.py migrate

نوشتن Serializer برای مدل Article

حالا فایل serializers.py را در اپ myapi بسازید. اگر این فایل وجود ندارد، خودتان ایجاد کنید. کد زیر را در آن بنویسید:

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()
    author = serializers.CharField(max_length=100)
    published_at = serializers.DateTimeField(read_only=True)

بیایید ببینیم هر خط چه می‌کند.

خط اول و دوم: کتابخانه‌های لازم را وارد می‌کنیم. serializers از DRF و Article از مدل‌های خودمان.

خط چهارم: کلاس ArticleSerializer را تعریف می‌کنیم که از serializers.Serializer ارث‌بری می‌کند.

خط پنجم تا نهم: فیلدهای Serializer را تعریف می‌کنیم. اسم فیلدها باید با اسم فیلدهای مدل یکی باشد.

read_only=True یعنی این فیلد فقط هنگام خروجی (تبدیل مدل به JSON) نمایش داده می‌شود. هنگام ورودی (JSON به مدل)، کاربر نباید مقدار آن را بفرستد. این برای فیلد id و published_at مناسب است.

max_length=200 هم مثل مدل، یک قانون اعتبارسنجی اضافه می‌کند.

تبدیل مدل به JSON با Serializer

حالا بیایید در محیط پایتون (با دستور python manage.py shell) تست کنیم که Serializer چطور کار می‌کند.

from myapi.models import Article
from myapi.serializers import ArticleSerializer

# ساخت یک مقاله نمونه
article = Article.objects.create(
    title="آموزش DRF",
    content="مطلب کامل درباره سریالایزرها...",
    author="مریم رضایی"
)

# تبدیل مدل به JSON
serializer = ArticleSerializer(article)
print(serializer.data)

خروجی چیزی شبیه این خواهد بود:

{
    'id': 1,
    'title': 'آموزش DRF',
    'content': 'مطلب کامل درباره سریالایزرها...',
    'author': 'مریم رضایی',
    'published_at': '2025-01-15T10:30:00Z'
}

توجه کنید که id و published_at که read_only=True دارند، در خروجی هستند اما شما هنگام ساخت مقاله مقدار آن‌ها را نفرستادید.

تبدیل JSON به مدل (ورودی)

حالا فرض کنید کاربر یک JSON می‌فرستد و ما می‌خواهیم یک مقاله جدید بسازیم.

import json
from myapi.serializers import ArticleSerializer

# داده ورودی از کاربر (مثلاً از درخواست POST)
data = {
    'title': 'مطلب جدید',
    'content': 'محتوای این مطلب...',
    'author': 'علی کریمی'
}

serializer = ArticleSerializer(data=data)
if serializer.is_valid():
    article = serializer.save()
    print(article.title)
else:
    print(serializer.errors)

اگر داده معتبر باشد، متد save() یک شیء مدل جدید می‌سازد و در دیتابیس ذخیره می‌کند.

یک مشکل مهم

در کد بالا، serializer.save() خطا می‌دهد. چرا؟

چون ما در ArticleSerializer فقط فیلدها را تعریف کرده‌ایم. اما به DRF نگفته‌ایم که چطور از این داده‌ها یک مدل Article بسازد.

برای حل این مشکل، باید دو متد create و update را در Serializer پیاده‌سازی کنیم. در زیرعنوان بعدی (تبدیل مدل به JSON و بالعکس) این کار را کامل یاد می‌گیرید.

فعلاً این کد را دارید:

class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()
    author = serializers.CharField(max_length=100)
    published_at = serializers.DateTimeField(read_only=True)
    
    def create(self, validated_data):
        return Article.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        instance.author = validated_data.get('author', instance.author)
        instance.save()
        return instance

با اضافه کردن create و update، متد save() کار می‌کند.

متدهای create، update و save در Serializer

در زیرعنوان قبل، یک Serializer نوشتیم اما وقتی خواستیم با save() داده را ذخیره کنیم، کار نکرد. دلیلش این بود که DRF نمی‌داند چطور داده اعتبارسنجی شده را به مدل تبدیل کند.

اینجا دو متد create و update وارد می‌شوند.

متد create – برای ساختن شیء جدید

وقتی کاربر یک درخواست POST می‌فرستد، داده جدید است. هنوز در دیتابیس وجود ندارد. Serializer باید یک رکورد جدید بسازد.

متد create این کار را انجام می‌دهد.

def create(self, validated_data):
    return Article.objects.create(**validated_data)

validated_data یک دیکشنری پایتون است. شامل داده‌ای که اعتبارسنجی را گذرانده. **validated_data این دیکشنری را باز می‌کند و به صورت آرگومان کلیدی به create مدل می‌فرستد.

متد update – برای بروزرسانی شیء موجود

وقتی کاربر یک درخواست PUT یا PATCH می‌فرستد، داده برای بروزرسانی یک رکورد موجود است. Serializer باید رکورد قدیمی را پیدا کند و فیلدهای آن را تغییر دهد.

متد update دو پارامتر دارد: instance (شیء قدیمی) و validated_data (داده جدید).

def update(self, instance, validated_data):
    instance.title = validated_data.get('title', instance.title)
    instance.content = validated_data.get('content', instance.content)
    instance.author = validated_data.get('author', instance.author)
    instance.save()
    return instance

متد get در دیکشنری validated_data مقدار را برمی‌گرداند. اگر آن کلید وجود نداشت، مقدار قبلی شیء (instance.title) را نگه می‌دارد. اینطوری فیلدهایی که کاربر نفرستاده، تغییر نمی‌کنند.

بعد از تغییر فیلدها، instance.save() را صدا می‌زنیم تا در دیتابیس ذخیره شود.

متد save – فراخوان خودکار create یا update

شما مستقیم create و update را صدا نمی‌زنید. در عوض، متد save() را صدا می‌زنید. خود DRF تشخیص می‌دهد که باید create اجرا شود یا update.

# برای ساختن شیء جدید
serializer = ArticleSerializer(data=data)
if serializer.is_valid():
    serializer.save()  # اینجا create اجرا می‌شود

# برای بروزرسانی شیء موجود
article = Article.objects.get(id=1)
serializer = ArticleSerializer(instance=article, data=data)
if serializer.is_valid():
    serializer.save()  # اینجا update اجرا می‌شود

اگر موقع ساخت Serializer، پارامتر instance را داده باشید، save() می‌داند که باید بروزرسانی کند. در غیر این صورت، می‌داند باید بسازد.

یک مثال کامل

بیایید یک Serializer کامل با هر دو متد بنویسیم:

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()
    author = serializers.CharField(max_length=100)
    published_at = serializers.DateTimeField(read_only=True)
    
    def create(self, validated_data):
        return Article.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        instance.author = validated_data.get('author', instance.author)
        instance.save()
        return instance

حالا این Serializer هم می‌تواند شیء جدید بسازد، هم شیء موجود را بروزرسانی کند.

 

آزمایش در محیط شل پایتون

from myapi.models import Article
from myapi.serializers import ArticleSerializer

# تست create
data = {'title': 'test', 'content': '...', 'author': 'ali'}
serializer = ArticleSerializer(data=data)
serializer.is_valid()  # True
article = serializer.save()  # یک رکورد جدید در دیتابیس ساخته می‌شود
print(article.id)  # 1

# تست update
article = Article.objects.get(id=1)
new_data = {'title': 'عنوان جدید', 'author': 'رضا'}
serializer = ArticleSerializer(instance=article, data=new_data)
serializer.is_valid()  # True
updated_article = serializer.save()  # رکورد با id=1 بروزرسانی می‌شود
print(updated_article.title)  # عنوان جدید
print(updated_article.author)  # رضا
print(updated_article.content)  # همان محتوای قبلی (تغییر نکرده)

نکته مهم: در مثال update، ما فقط title و author را فرستادیم. content را نفرستادیم. متد update با get مقدار قبلی را نگه داشت. این رفتار درست و مورد انتظار است.

در بخش های بعدی، با ModelSerializer آشنا می‌شوید که این متدهای create و update را به صورت خودکار برای شما می‌نویسد.

اعتبارسنجی (Validation) در Serializer

وقتی کاربر داده‌ای به API شما می‌فرستد، نمی‌توانید به آن اعتماد کنید. ممکن است فیلد اجباری را خالی بگذارد. ممکن است به جای عدد، متن بفرستد. یا ایمیل را بدون @ وارد کند.

اعتبارسنجی یعنی بررسی همین موارد قبل از ذخیره در دیتابیس.

اعتبارسنجی خودکار فیلدها

وقتی در Serializer یک فیلد تعریف می‌کنید، DRF خودکار برخی اعتبارسنجی‌ها را انجام می‌دهد.

class ArticleSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=200)
    age = serializers.IntegerField(min_value=18, max_value=99)
    email = serializers.EmailField()

در این مثال:

title نمی‌تواند بیشتر از ۲۰۰ کاراکتر باشد.

age باید بین ۱۸ و ۹۹ باشد.

email حتماً باید فرمت یک ایمیل معتبر را داشته باشد.

این اعتبارسنجی‌ها بدون هیچ کد اضافه‌ای کار می‌کنند.

اعتبارسنجی در سطح یک فیلد (Field-level validation)

گاهی قانون خاصی دارید که با اعتبارسنجی خودکار امکان‌پذیر نیست. مثلاً می‌خواهید عنوان مقاله با حرف بزرگ شروع شود.

برای این کار، یک متد به اسم validate_<field_name> در Serializer بنویسید:

class ArticleSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=200)
    
    def validate_title(self, value):
        if not value[0].isupper():
            raise serializers.ValidationError("عنوان باید با حرف بزرگ شروع شود")
        return value

اگر مقدار معتبر نبود، خطا می‌دهید. اگر معتبر بود، همان مقدار را برمی‌گردانید.

اعتبارسنجی در سطح کل شیء (Object-level validation)

گاهی قانون شما به بیش از یک فیلد نیاز دارد. مثلاً تاریخ پایان نباید از تاریخ شروع کوچک‌تر باشد.

برای این کار، متد validate را در Serializer بازنویسی می‌کنید:

class EventSerializer(serializers.Serializer):
    start_date = serializers.DateTimeField()
    end_date = serializers.DateTimeField()
    
    def validate(self, data):
        if data['start_date'] > data['end_date']:
            raise serializers.ValidationError("تاریخ پایان نباید از تاریخ شروع زودتر باشد")
        return data

data یک دیکشنری از تمام داده‌های اعتبارسنجی شده است. می‌توانید هر فیلدی را در آن بررسی کنید.

خطاهای اعتبارسنجی چه شکلی هستند؟

وقتی serializer.is_valid() را صدا می‌زنید و داده نامعتبر است، می‌توانید خطاها را در serializer.errors ببینید.

serializer = ArticleSerializer(data={'title': 'a'})
if not serializer.is_valid():
    print(serializer.errors)

خروجی چیزی شبیه این است:

{
    'title': ['Ensure this field has at least 2 characters.'],
    'author': ['This field is required.']
}

این خروجی استاندارد است. فرانت‌اند می‌داند چطور آن را پردازش کند.

ترتیب اجرای اعتبارسنجی‌ها

وقتی is_valid() صدا زده می‌شود، DRF این مراحل را به ترتیب انجام می‌دهد:

  1. اعتبارسنجی خودکار هر فیلد (مثل max_length، min_value)
  2. متدهای validate_<field_name> برای هر فیلد
  3. متد validate در سطح کل شیء

اگر هر مرحله خطایی بدهد، مراحل بعدی اجرا نمی‌شوند.

مثال کامل از یک Serializer با اعتبارسنجی

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()
    author = serializers.CharField(max_length=100)
    
    def validate_title(self, value):
        if len(value) < 5:
            raise serializers.ValidationError("عنوان حداقل باید ۵ کاراکتر باشد")
        return value
    
    def validate(self, data):
        if "badword" in data['title'].lower() or "badword" in data['content'].lower():
            raise serializers.ValidationError("مطلب شامل کلمات ممنوعه است")
        return data
    
    def create(self, validated_data):
        return Article.objects.create(**validated_data)

در این Serializer:

  • فیلد title خودکار حداکثر ۲۰۰ کاراکتر را چک می‌کند.
  • متد validate_title حداقل ۵ کاراکتر بودن را بررسی می‌کند.
  • متد validate کلمات ممنوعه را در عنوان و محتوا چک می‌کند.

نکته: اعتبارسنجی در سمت سرور اجباری است. هرگز به اعتبارسنجی سمت فرانت‌اند (مثل جاوااسکریپت) اعتماد نکنید. کاربر حرفه‌ای می‌تواند مستقیماً به API شما درخواست بفرستد و اعتبارسنجی مرورگر را دور بزند.

در  ModelSerializer این اعتبارسنجی‌ها بر اساس فیلدهای مدل به صورت خودکار اعمال خواهد شد.

ModelSerializer در DRF

تا اینجا با Serializer پایه کار کردیم. برای هر مدل، باید فیلدها را دوباره تعریف می‌کردیم. باید create و update را دستی می‌نوشتیم. این کار برای مدل‌های کوچک شاید ساده باشد. اما وقتی تعداد مدل‌ها و فیلدها زیاد شود، کدنویسی تکراری و خسته‌کننده می‌شود.

ModelSerializer این مشکل را حل می‌کند.

ModelSerializer چیست؟

ModelSerializer یک کلاس آماده در DRF است. از Serializer معمولی ارث‌بری می‌کند. اما خیلی از کارها را به صورت خودکار انجام می‌دهد.

تفاوت اصلی با Serializer معمولی در سه چیز است:

اول: تعریف خودکار فیلدها

شما فقط مشخص می‌کنید از کدام مدل استفاده کنید. DRF خودکار فیلدهای آن مدل را می‌خواند و به Serializer اضافه می‌کند.

دوم: create و update خودکار

دیگر نیازی به نوشتن متدهای create و update نیست. DRF خودش می‌داند چطور داده را در دیتابیس ذخیره یا بروزرسانی کند.

سوم: اعتبارسنجی خودکار بر اساس مدل

محدودیت‌های فیلدهای مدل (مثل max_length، null، unique) خودکار به Serializer منتقل می‌شوند.

نوشتن اولین ModelSerializer

به جای ۱۵ خط کد قبلی، با این چند خط کار تمام می‌شود:

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author', 'published_at']

بیایید ببینیم چه خبر است.

کلاس ArticleSerializer از ModelSerializer ارث‌بری می‌کند. داخل آن یک کلاس داخلی به اسم Meta می‌سازیم. در Meta دو چیز را مشخص می‌کنیم:

  • model: نام مدلی که می‌خواهیم سریالایزر برای آن بنویسیم.
  • fields: لیست فیلدهایی که می‌خواهیم در خروجی و ورودی باشند.

با همین چند خط، DRF تمام کارهای زیر را انجام می‌دهد:

  • فیلدهای id، title، content، author، published_at را با نوع مناسب تعریف می‌کند.
  • create و update را پیاده‌سازی می‌کند.
  • اعتبارسنجی‌های مدل (مثل max_length=200 برای title) را اعمال می‌کند.

گزینه‌های رایج در Meta

استفاده از fields = '__all__'

اگر همه فیلدهای مدل را می‌خواهید، می‌توانید بنویسید:

class Meta:
    model = Article
    fields = '__all__'

این کار سریع است اما دقت کنید. فیلدهای حساس مثل password یا is_admin را هم در معرض دید قرار می‌دهد.

حذف بعضی فیلدها با exclude

به جای لیست کردن فیلدهای مورد نظر، می‌توانید بگویید کدام فیلدها را نمی‌خواهید:

class Meta:
    model = Article
    exclude = ['published_at']

فقط خواندنی کردن بعضی فیلدها با read_only_fields

class Meta:
    model = Article
    fields = ['id', 'title', 'content', 'author', 'published_at']
    read_only_fields = ['id', 'published_at']

مقایسه کد نوشته شده

Serializer معمولی (قبلی): ۲۵ خط کد
ModelSerializer (حالی): ۷ خط کد

این تفاوت در پروژه‌های واقعی خیلی بیشتر می‌شود.

اضافه کردن اعتبارسنجی سفارشی در ModelSerializer

حتی با ModelSerializer هم می‌توانید اعتبارسنجی سفارشی اضافه کنید. دقیقاً مثل Serializer معمولی:

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author', 'published_at']
    
    def validate_title(self, value):
        if len(value) < 5:
            raise serializers.ValidationError("عنوان حداقل باید ۵ کاراکتر باشد")
        return value
    
    def validate(self, data):
        if "badword" in data['title'].lower():
            raise serializers.ValidationError("عنوان شامل کلمات ممنوعه است")
        return data

نکته مهم: fields و exclude را همزمان استفاده نکنید

این دو گزینه با هم تداخل دارند. فقط یکی از آنها را به کار ببرید. در غیر این صورت DRF خطا می‌دهد.

تمرین برای شما

مدل Product با فیلدهای name، price، stock، created_at بسازید. سپس یک ModelSerializer برای آن بنویسید. created_at را فقط خواندنی (read_only) قرار دهید.

آشنایی با to_representation و to_internal_value

تا الان یاد گرفتید که Serializer داده را از مدل به JSON و بالعکس تبدیل می‌کند. اما گاهی نیاز دارید در این تبدیل، دستی تغییر ایجاد کنید. مثلاً تاریخ را به فرمت دیگری نمایش دهید. یا قبل از ذخیره در دیتابیس، متن را پاکسازی کنید.

اینجا دو متد to_representation و to_internal_value وارد می‌شوند.

to_representation – تغییر خروجی (مدل به JSON)

این متد وقتی اجرا می‌شود که می‌خواهید مدل را به JSON تبدیل کنید. قبل از اینکه DRF خروجی نهایی را بسازد، این متد صدا زده می‌شود.

کاربردها:

  • تغییر فرمت تاریخ
  • حذف بعضی فیلدها در خروجی
  • اضافه کردن فیلد محاسباتی (مثل سن از روی تاریخ تولد)
  • تغییر نام فیلدها در خروجی

مثال: نمایش تاریخ به صورت شمسی

فرض کنید می‌خواهید فیلد published_at را که به صورت میلادی در دیتابیس ذخیره شده، به صورت شمسی در خروجی نشان دهید.

from rest_framework import serializers
from .models import Article
import datetime

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author', 'published_at']
    
    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # تبدیل تاریخ میلادی به شمسی (نمایش ساده)
        if instance.published_at:
            representation['published_at'] = instance.published_at.strftime('%Y/%m/%d')
        return representation

instance خود شیء مدل است. ابتدا با super().to_representation(instance) خروجی عادی را می‌گیریم. سپس فیلد published_at را با فرمت جدید جایگزین می‌کنیم.

مثال: اضافه کردن فیلد محاسباتی

def to_representation(self, instance):
    representation = super().to_representation(instance)
    representation['word_count'] = len(instance.content.split())
    return representation

این کد یک فیلد جدید به اسم word_count به خروجی اضافه می‌کند که تعداد کلمات محتوا را نشان می‌دهد.

to_internal_value – تغییر ورودی (JSON به مدل)

این متد وقتی اجرا می‌شود که داده JSON از کاربر می‌رسد. قبل از اعتبارسنجی و ذخیره، شما می‌توانید داده را تغییر دهید.

کاربردها:

  • پاکسازی متن (حذف فضاهای خالی اضافه)
  • تبدیل فرمت تاریخ ورودی
  • تغییر حروف به کوچک یا بزرگ
  • اضافه کردن مقدار پیش‌فرض

مثال: پاکسازی عنوان

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author']
    
    def to_internal_value(self, data):
        if 'title' in data:
            data['title'] = data['title'].strip()  # حذف فضاهای خالی اول و آخر
        return super().to_internal_value(data)

مثال: تبدیل حروف به کوچک برای ایمیل

def to_internal_value(self, data):
    if 'email' in data:
        data['email'] = data['email'].lower()
    return super().to_internal_value(data)

تفاوت این دو متد با validate

validate برای اعتبارسنجی است. اگر داده نامعتبر است، خطا می‌دهید.

to_representation و to_internal_value برای تغییر داده هستند. نه برای خطا دادن. این متدها همیشه باید داده تغییر یافته را برگردانند.

مثال کامل و کاربردی

فرض کنید می‌خواهید:

عنوان مقاله را در خروجی با حروف بزرگ نشان دهید

قبل از ذخیره، فضاهای خالی عنوان را حذف کنید

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'author']
    
    def to_internal_value(self, data):
        # پاکسازی ورودی قبل از ذخیره
        if 'title' in data:
            data['title'] = data['title'].strip()
        return super().to_internal_value(data)
    
    def to_representation(self, instance):
        # تغییر خروجی برای نمایش
        representation = super().to_representation(instance)
        if representation['title']:
            representation['title'] = representation['title'].upper()
        return representation

جمع بندی

متد زمان اجرا کاربرد اصلی
to_representation خروجی (مدل → JSON) تغییر نمایش، اضافه کردن فیلد محاسباتی
to_internal_value ورودی (JSON → مدل) پاکسازی، تبدیل فرمت، تغییر مقدار قبل از ذخیره
validate بین این دو اعتبارسنجی، خطا دادن

نکته مهم: کمتر از این متدها استفاده کنید. اگر کار شما با validate یا تغییر در خود مدل امکان‌پذیر است، آن روش را ترجیح دهید. این متدها را فقط برای موارد خاصی که راه دیگری ندارید، به کار ببرید.

جمع‌بندی درس سریالایزرها

در این درس، با قلب DRF آشنا شدید.

یاد گرفتید Serializer چیست و چرا بدون آن، کار با مدل‌ها و تبدیل داده به JSON سخت و تکراری می‌شود.

یک مدل Article ساختید و برای آن Serializer معمولی نوشتید. دیدید که باید فیلدها را یکی یکی تعریف کنید و متدهای create و update را خودتان پیاده‌سازی نمایید.

سپس با اعتبارسنجی آشنا شدید. یاد گرفتید چطور در سه سطح اعتبارسنجی کنید: خودکار فیلدها، متدهای validate_<field_name>، و متد validate برای چند فیلد با هم.

بعد از آن ModelSerializer را معرفی کردیم. با چند خط کد، تمام کارهای قبلی را انجام داد. بدون نیاز به تعریف مجدد فیلدها و نوشتن create و update.

در انتها، با دو متد to_representation و to_internal_value آشنا شدید. دیدید که چطور می‌توانید قبل از خروجی یا ورودی، داده را تغییر دهید.

از این درس چه چیزی باید به خاطر بسپارید؟

  1. هرگاه با مدل‌های جنگو کار می‌کنید، از ModelSerializer استفاده کنید. نه Serializer معمولی.
  2. اعتبارسنجی را جدی بگیرید. هیچگاه به داده ورودی کاربر اعتماد نکنید.
  3. create و update در ModelSerializer خودکار است. اما اگر نیاز به رفتار خاصی دارید، می‌توانید آنها را بازنویسی کنید.
  4. to_representation و to_internal_value ابزارهای قدرتمندی هستند. اما تا حد امکان از آنها کم استفاده کنید.

درس بعدی چیست؟

حالا که Serializer را بلدید، وقت آن رسیده که ویوهای حرفه‌ای‌تر بنویسید. در درس بعد، با کلاس‌بیس ویوها (APIView) آشنا می‌شوید. می‌بینید چطور Serializer را در یک ویو کلاسی استفاده کنید و کد خود را مرتب‌تر و قابل استفاده مجددتر بنویسید.

برای درس بعد، همین ArticleSerializer که الان دارید، کافی است. آن را نگه دارید.