Skip to main content

Data Serialization Formats

The serialization framework in this codebase provides a unified interface for converting Django querysets into various data formats and reconstructing model instances from those formats. This is primarily used for creating fixtures, exporting data, and inter-process communication.

The system is built around a central registry in django.core.serializers that maps format names (like json, xml, yaml) to specific serializer modules.

Core Serialization Interface

The primary entry points are the serialize and deserialize functions located in django/core/serializers/__init__.py.

Serializing Data

The serialize function takes a format name and a queryset, returning the serialized data as a string (or writing it to a stream).

from django.core import serializers
from myapp.models import MyModel

# Serialize to a JSON string
data = serializers.serialize("json", MyModel.objects.all())

# Serialize to a file-like object (stream)
with open("data.xml", "w") as f:
serializers.serialize("xml", MyModel.objects.all(), stream=f)

Deserializing Data

The deserialize function returns an iterator of DeserializedObject instances. These objects are wrappers around unsaved model instances.

for deserialized_obj in serializers.deserialize("json", data):
# The actual model instance is in .object
print(deserialized_obj.object)
# You must call .save() to persist it to the database
deserialized_obj.save()

The Serialization Lifecycle

All serializers inherit from the abstract Serializer class in django/core/serializers/base.py. This class implements a template pattern that defines the lifecycle of serializing a queryset:

  1. start_serialization(): Initializes the output (e.g., opening a root XML tag or a JSON array).
  2. start_object(obj): Called at the beginning of each model instance.
  3. Field Handling:
    • handle_field(obj, field): Processes standard fields (integers, strings, etc.).
    • handle_fk_field(obj, field): Processes ForeignKey relationships.
    • handle_m2m_field(obj, field): Processes ManyToManyField relationships.
  4. end_object(obj): Finalizes the object representation.
  5. end_serialization(): Closes the output.

Supported Formats

Python Objects

The PythonSerializer in django/core/serializers/python.py is the foundation for many other formats. It converts a queryset into a list of standard Python dictionaries. It is marked as internal_use_only = True because its output is intended to be consumed by other serializers (like JSON or YAML) rather than used directly as a public format.

JSON and JSON Lines

The JSONSerializer (django/core/serializers/json.py) extends the Python serializer. It uses DjangoJSONEncoder to handle Django-specific types like datetime, Decimal, and UUID that the standard json module cannot process.

The JSONLSerializer (django/core/serializers/jsonl.py) implements the JSON Lines format, where each object is serialized as a single JSON object on a new line. This is particularly useful for large datasets as it allows for line-by-line processing.

YAML

The YAML implementation in django/core/serializers/pyyaml.py requires the PyYAML library. It uses a custom DjangoSafeDumper to ensure that Decimal and time objects are represented as strings, improving interoperability with other languages that might consume the YAML.

XML

Unlike the others, the XMLSerializer (django/core/serializers/xml_serializer.py) does not inherit from the Python serializer. It uses SimplerXMLGenerator to produce a structured XML document. It handles nested natural keys by rolling them out as sub-elements within the <field> tag.

Natural Keys

Natural keys allow objects to be identified by a unique set of attributes rather than a database-specific primary key (like an auto-incrementing ID). This is critical for moving data between databases where IDs might conflict.

To use natural keys during serialization, pass the following flags to serialize:

  • use_natural_foreign_keys=True: Uses the natural_key() method of related models to represent foreign keys.
  • use_natural_primary_keys=True: Omits the pk field in the output if the model itself defines a natural_key() method.

The Deserializer uses Model.objects.get_by_natural_key() to resolve these references back into database IDs during the import process.

DeserializedObject and Persistence

When you iterate over a deserializer, you receive DeserializedObject instances (django/core/serializers/base.py).

The save() method on this class is unique:

  • It calls models.Model.save_base(..., raw=True).
  • Setting raw=True tells Django to skip custom save() logic and signals to listeners (via pre_save/post_save) that the data is being loaded from a serialized source.
  • It handles the saving of Many-to-Many relationships after the primary object has been saved.

Advanced Configuration

Custom Serializers

You can register custom serialization formats using the SERIALIZATION_MODULES setting. This dictionary maps the format name to the module path containing the Serializer and Deserializer classes.

# settings.py
SERIALIZATION_MODULES = {
"csv": "myapp.serializers.csv_serializer",
}

Large Datasets

For large querysets, the serialize method supports a progress_output parameter. If provided with a stream (like sys.stdout), it uses the ProgressBar class from django/core/serializers/base.py to provide visual feedback during the process.

import sys
serializers.serialize(
"json",
LargeModel.objects.all(),
progress_output=sys.stdout,
object_count=LargeModel.objects.count()
)