Skip to main content

Serializer

The serializer submodule validates input and converts between JSON data and Python objects — typically SQLAlchemy models.

Serializer

Constructor

Serializer(
data: str | None = None,
instance: Any | None = None,
required: bool = True,
nullable: bool = False,
many: bool = False,
context: dict | None = None,
read_only: bool = False,
write_only: bool = False,
)
  • data — raw JSON string to validate
  • instance — object (or list of objects when many=True) to serialize
  • context — arbitrary dict available as self.context in overridden methods
  • many — handle a list of objects

Properties

PropertyDescription
instanceThe instance being serialized (settable)
validated_dataValidated fields after is_valid()
raw_dataThe raw JSON string input (settable)
contextArbitrary context passed to validation
dataSerialized representation of the instance(s); excludes write_only fields. None when no instance is set

Methods

MethodDescription
is_valid()Parse raw_data and validate it; raises ValidationException on failure
validate(attr: dict) -> dictValidate a Python dict; base implementation strips read_only fields
schema() -> dictGenerate the JSON Schema for the serializer
create(session, validated_data)Build Meta.model(**validated_data), session.add, commit, refresh; returns the instance
save(session)Requires validated_data (call is_valid() first); calls create(session, validated_data)
update(session, instance, validated_data)Set attributes on instance, commit, refresh
to_representation(instance) -> dictConvert an instance to a dict using SQLAlchemy inspection

Validation flow

is_valid() reads raw_data (raising ValidationException when it is empty), parses the JSON string, then calls validate(...) — so a Python override of validate is honored. The base validate() checks the data against the JSON schema generated from the declared fields (including format validation) and removes read_only fields. The result is stored in validated_data.

from oxapy import serializer


class Cred(serializer.Serializer):
email = serializer.EmailField()
password = serializer.CharField(min_length=8)


cred = Cred('{"email": "test@gmail.com", "password": "password"}')
cred.is_valid()
print(cred.validated_data)

When overriding validate, call super().validate(attr) to keep schema validation and read_only stripping:

def validate(self, attr: dict) -> dict:
attr["principal_amount"] = Decimal(attr["principal_amount"])
return super().validate(attr)

Model binding

For create() and save() to work, declare the model in a Meta class:

class UserSerializer(serializer.Serializer):
email = serializer.EmailField()
password = serializer.CharField(min_length=8)

class Meta:
model = User

create() then:

  1. Builds the instance: Meta.model(**validated_data)
  2. session.add(instance)
  3. session.commit()
  4. session.refresh(instance)

and returns the instance. save(session) reads validated_data (raising Exception when is_valid() was not called first) and delegates to create(session, validated_data), so overridden create methods are honored.

Serialization with SQLAlchemy

to_representation(instance) uses SQLAlchemy's inspection API:

  • It iterates the model's mapped columns and includes each value whose name matches a declared field, skipping write_only fields.
  • It iterates the model's relationships; for each one that matches a declared nested serializer field, it sets the field's context and instance and stores the nested serializer's data (a list when many=True).

Override it to add computed values:

def to_representation(self, instance: Loan):
data = super().to_representation(instance)
data.update({"principal_amount": float(data["principal_amount"])})
return data

Fields

Field types

FieldPurpose
CharFieldStrings
IntegerFieldIntegers
NumberFieldFloats / numbers
EmailFieldEmail addresses
BooleanFieldBooleans
DateFieldDates
DateTimeFieldDatetimes
EnumFieldRestricted value sets
UUIDFieldUUIDs

Field options

OptionDescription
requiredField must be present (default True)
nullableNone is allowed (default False)
manyField holds a list
lengthExact length
min_length / max_lengthLength bounds
patternRegex pattern
enum_valuesAllowed values for EnumField
formatValue format
read_onlyExcluded from validation/deserialization
write_onlyExcluded from serialization

Fields are regular Python classes, so they can be subclassed for domain-specific validation:

class PhoneNumberSerializer(serializer.CharField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.pattern = r"^(?:\+261|0)(32|33|34|37|38)\d{7}$"

ValidationException

Raised by is_valid() when the input is missing or invalid.