~10 دقیقه مطالعه • بروزرسانی ۲۴ اسفند ۱۴۰۴
مقدمه
فریمورک سریالسازی Django ابزاری قدرتمند برای تبدیل مدلهای Django به فرمتهای مختلف داده است. این فرمتها معمولاً متنی هستند—مثل JSON یا XML—و برای انتقال داده، بکاپگیری یا ارتباط با سرویسهای خارجی استفاده میشوند.
سریالسازی یعنی تبدیل یک شیء Django به دادهٔ ساختیافته. دسریالسازی یعنی تبدیل آن دادهها به شیء Django.
سریالسازی دادهها
سادهترین روش سریالسازی:
from django.core import serializers
data = serializers.serialize("json", SomeModel.objects.all())
آرگومان اول فرمت (json، xml، yaml و …) و آرگومان دوم یک QuerySet یا هر iterable از مدلهاست.
استفادهٔ مستقیم از Serializer
میتوانید یک Serializer را مستقیماً دریافت و استفاده کنید:
JSONSerializer = serializers.get_serializer("json")
json_serializer = JSONSerializer()
json_serializer.serialize(queryset)
data = json_serializer.getvalue()
این روش برای نوشتن مستقیم در فایل یا stream مناسب است:
with open("file.json", "w") as out:
json_serializer.serialize(SomeModel.objects.all(), stream=out)
اگر فرمت ناشناخته باشد، Django خطای SerializerDoesNotExist میدهد.
سریالسازی بخشی از فیلدها
میتوانید فقط برخی فیلدها را سریالسازی کنید:
data = serializers.serialize(
"json",
SomeModel.objects.all(),
fields=["name", "size"]
)
کلید اصلی (pk) همیشه سریالسازی میشود، حتی اگر در فهرست فیلدها نباشد.
نکته: اگر فیلدهای ضروری مدل سریالسازی نشده باشند، دسریالسازی ممکن است شکست بخورد.
سریالسازی مدلهای ارثبری
ارثبری از کلاس پایهٔ abstract
در این حالت نیازی به کار خاصی نیست؛ سریالسازی کامل انجام میشود.
ارثبری چندجدولی (Multi‑Table)
در این حالت Django فقط فیلدهای تعریفشده در مدل فرزند را سریالسازی میکند. مثال:
class Place(models.Model):
name = models.CharField(max_length=50)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
سریالسازی Restaurant فقط فیلد serves_hot_dogs را شامل میشود:
serializers.serialize("json", Restaurant.objects.all())
برای سریالسازی کامل:
all_objects = [*Restaurant.objects.all(), *Place.objects.all()]
data = serializers.serialize("json", all_objects)
دسریالسازی دادهها
دسریالسازی مشابه سریالسازی است:
for obj in serializers.deserialize("json", data):
do_something_with(obj)
اما خروجی DeserializedObject است، نه شیء Django.
ذخیرهٔ شیء دسریالشده
برای ذخیره:
for deserialized in serializers.deserialize("json", data):
deserialized.save()
اگر pk موجود نباشد یا null باشد، یک شیء جدید ساخته میشود.
نادیده گرفتن فیلدهای نامعتبر
اگر دادهٔ سریالشده شامل فیلدهای ناموجود باشد، Django خطای DeserializationError میدهد مگر اینکه:
serializers.deserialize("json", data, ignorenonexistent=True)
فرمتهای سریالسازی
| فرمت | توضیح |
|---|---|
| json | سریالسازی استاندارد JSON |
| jsonl | فرمت JSON Lines |
| xml | فرمت XML ساده |
| yaml | نیازمند نصب PyYAML |
نمونهٔ سریالسازی XML
فیلدهای رابطهای
ForeignKey:
9
ManyToMany:
کاراکترهای کنترل
XML 1.0 برخی کاراکترهای کنترل را نمیپذیرد.
اگر دادهٔ شما شامل این کاراکترها باشد، سریالسازی با ValueError شکست میخورد.
جمعبندی
فریمورک سریالسازی Django ابزاری قدرتمند برای انتقال داده، بکاپگیری و یکپارچهسازی سیستمهاست. با شناخت نحوهٔ سریالسازی مدلها، مدیریت ارثبری، روابط، و دسریالسازی امن، میتوانید دادهها را بهصورت ساختیافته و قابلاعتماد مدیریت کنید.
سریالسازی JSON در Django
JSON یکی از رایجترین فرمتها برای تبادل داده است. Django خروجی JSON را بهصورت ساختاریافته و استاندارد تولید میکند. نمونهٔ خروجی JSON برای یک مدل Session به شکل زیر است:
[
{
"pk": "4b678b301dfd8a4e0dad910de3ae245b",
"model": "sessions.session",
"fields": {
"expire_date": "2013-01-16T08:16:59.844Z"
}
}
]
در JSON:
- pk کلید اصلی است
- model نام مدل بهصورت app_label.model_name است
- fields شامل فیلدهای مدل و مقدار آنهاست
سریالسازی روابط
- ForeignKey → مقدار pk شیء مرتبط
- ManyToMany → لیستی از pkها
سریالسازی انواع دادهٔ خاص (Custom Types)
اگر مدل شما شامل نوع دادهٔ سفارشی باشد، Django نمیتواند آن را مستقیماً به JSON تبدیل کند. در این حالت باید یک JSONEncoder سفارشی بسازید:
from django.core.serializers.json import DjangoJSONEncoder
class LazyEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, YourCustomType):
return str(obj)
return super().default(obj)
و سپس:
from django.core.serializers import serialize
serialize("json", SomeModel.objects.all(), cls=LazyEncoder)
GeoDjango نیز یک GeoJSON Serializer اختصاصی ارائه میدهد.
DjangoJSONEncoder
DjangoJSONEncoder یک نسخهٔ توسعهیافته از JSONEncoder است که انواع زیر را پشتیبانی میکند:
- datetime → فرمت استاندارد ECMA-262
- date → YYYY-MM-DD
- time → HH:MM:ss.sss
- timedelta → فرمت ISO-8601
- Decimal، UUID، Promise → تبدیل به رشته
سریالسازی JSONL
JSONL یا JSON Lines برای دادههای حجیم بسیار مناسب است. در این فرمت هر خط یک JSON مستقل است:
{"pk": "...", "model": "sessions.session", "fields": {...}}
{"pk": "...", "model": "sessions.session", "fields": {...}}
{"pk": "...", "model": "sessions.session", "fields": {...}}
مزیت اصلی JSONL این است که میتوان دادهها را خطبهخط پردازش کرد بدون اینکه کل فایل در حافظه بارگذاری شود.
سریالسازی YAML
YAML شباهت زیادی به JSON دارد اما خوانایی بیشتری دارد. برای استفاده از YAML باید PyYAML نصب باشد.
- model: sessions.session
pk: 4b678b301dfd8a4e0dad910de3ae245b
fields:
expire_date: 2013-01-16 08:16:59.844560+00:00
روابط نیز مانند JSON با pk یا لیست pkها نمایش داده میشوند.
ساخت Serializer سفارشی (مثال: CSV)
Django اجازه میدهد فرمتهای سریالسازی سفارشی بسازید. در این مثال یک Serializer و Deserializer برای CSV تعریف میکنیم.
Serializer سفارشی
# path/to/custom_csv_serializer.py
import csv
from django.apps import apps
from django.core import serializers
from django.core.serializers.base import DeserializationError
class Serializer(serializers.python.Serializer):
def get_dump_object(self, obj):
dumped = super().get_dump_object(obj)
row = [dumped["model"], str(dumped["pk"])]
row += [str(value) for value in dumped["fields"].values()]
return ",".join(row), dumped["model"]
def end_object(self, obj):
dumped_str, model = self.get_dump_object(obj)
if self.first:
fields = [f.name for f in apps.get_model(model)._meta.fields]
header = ",".join(fields)
self.stream.write(f"model,{header}\n")
self.stream.write(f"{dumped_str}\n")
Deserializer سفارشی
class Deserializer(serializers.python.Deserializer):
def __init__(self, stream_or_string, **options):
if isinstance(stream_or_string, bytes):
stream_or_string = stream_or_string.decode()
if isinstance(stream_or_string, str):
stream_or_string = stream_or_string.splitlines()
try:
objects = csv.DictReader(stream_or_string)
except Exception as exc:
raise DeserializationError() from exc
super().__init__(objects, **options)
def _handle_object(self, obj):
try:
model_fields = apps.get_model(obj["model"])._meta.fields
obj["fields"] = {
field.name: obj[field.name]
for field in model_fields
if field.name in obj
}
yield from super()._handle_object(obj)
except Exception as exc:
raise DeserializationError(f"Error deserializing object: {exc}") from exc
افزودن فرمت به تنظیمات Django
SERIALIZATION_MODULES = {
"csv": "path.to.custom_csv_serializer",
"json": "django.core.serializers.json",
}
جمعبندی
Django ابزارهای قدرتمندی برای سریالسازی دادهها ارائه میدهد—از JSON و XML گرفته تا JSONL و YAML. با استفاده از DjangoJSONEncoder میتوان انواع دادهٔ پیچیده را مدیریت کرد. همچنین امکان ساخت Serializerهای سفارشی مانند CSV وجود دارد که برای یکپارچهسازی سیستمها یا خروجیگیری داده بسیار مفید است.
Natural Keys چیست؟
بهطور پیشفرض، Django هنگام سریالسازی روابط ForeignKey و ManyToMany از مقدار Primary Key استفاده میکند. این روش معمولاً مناسب است، اما در برخی موارد مشکلساز میشود—بهخصوص زمانی که مدلهایی مانند ContentType، User، Group یا Permission درگیر باشند که PK آنها قابل پیشبینی نیست.
برای حل این مشکل، Django قابلیتی به نام Natural Keys ارائه میدهد. Natural Key یک تاپل از فیلدها است که بدون نیاز به PK میتواند یک شیء را بهطور یکتا شناسایی کند.
چرا Natural Keys مهم هستند؟
- وابسته نبودن به PKهای غیرقابل پیشبینی
- خوانایی بهتر دادههای سریالشده
- قابلحمل بودن دادهها بین محیطهای مختلف
- جلوگیری از خطاهای IntegrityError هنگام بارگذاری fixtureها
هشدار: هرگز اشیاء تولیدشدهٔ خودکار (مثل Permission یا ContentType) را در fixture قرار ندهید.
دسریالسازی Natural Keys
برای استفاده از Natural Key در دسریالسازی، باید یک Manager سفارشی با متد get_by_natural_key() تعریف کنید.
مثال:
class PersonManager(models.Manager):
def get_by_natural_key(self, first_name, last_name):
return self.get(first_name=first_name, last_name=last_name)
سپس مدل را به این Manager مجهز میکنیم:
class Person(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
objects = PersonManager()
class Meta:
constraints = [
models.UniqueConstraint(
fields=["first_name", "last_name"],
name="unique_first_last_name",
),
]
حالا Book میتواند نویسنده را با Natural Key ارجاع دهد:
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": ["Douglas", "Adams"]
}
}
Django هنگام دسریالسازی، مقدار ["Douglas", "Adams"] را به کمک get_by_natural_key() به یک Person واقعی تبدیل میکند.
سریالسازی Natural Keys
برای اینکه Django هنگام سریالسازی از Natural Key استفاده کند، باید متد natural_key() را در مدل تعریف کنید:
def natural_key(self):
return (self.first_name, self.last_name)
سپس هنگام سریالسازی:
serializers.serialize(
"json",
[book1, book2],
use_natural_foreign_keys=True,
use_natural_primary_keys=True
)
تفاوت دو گزینه:
- use_natural_foreign_keys=True → روابط FK با Natural Key سریال میشوند
- use_natural_primary_keys=True → خود شیء بدون PK سریال میشود
این کار زمانی مفید است که PK در دیتابیس مقصد ممکن است متفاوت باشد.
Natural Keys و Forward References
گاهی یک شیء به شیء دیگری ارجاع میدهد که هنوز دسریال نشده است. این حالت را forward reference مینامند.
برای مدیریت آن:
objs_with_deferred_fields = []
for obj in serializers.deserialize("json", data, handle_forward_references=True):
obj.save()
if obj.deferred_fields is not None:
objs_with_deferred_fields.append(obj)
for obj in objs_with_deferred_fields:
obj.save_deferred_fields()
نکته: فیلد FK باید null=True باشد.
کنترل ترتیب سریالسازی با Dependencies
اگر Natural Key یک مدل به مدل دیگری وابسته باشد، باید ترتیب سریالسازی را مشخص کنید.
مثال:
def natural_key(self):
return (self.name,) + self.author.natural_key()
natural_key.dependencies = ["example_app.person"]
این کار تضمین میکند که Person قبل از Book سریال شود.
جمعبندی
Natural Keys در Django راهی قدرتمند برای سریالسازی دادهها بدون وابستگی به Primary Key هستند.
با تعریف natural_key() و get_by_natural_key() میتوانید دادههایی قابلحمل، خوانا و مستقل از PK تولید کنید.
همچنین با مدیریت forward references و dependencies میتوانید سریالسازی پیچیده را نیز کنترل کنید.
نوشته و پژوهش شده توسط دکتر شاهین صیامی