ValidatedData¶
ValidatedData is the dict subclass that Serializer.validated_data
hands back when a payload has cleared validation. It behaves as a
plain dict everywhere a dict is expected, and adds two pieces of
sugar that make request handlers easier to read: attribute access
and a JSON helper that already knows about Decimal, datetime,
date, time, and UUID.
Reading and writing¶
ValidatedData is a dict subclass, so vd["name"], vd.get("name"),
**vd, and dict(vd) all work the way they do on a regular dict.
The class adds attribute access on top.
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
data.name # same as data["name"]
data["name"]
data.get("name")
{**data} # plain dict copy
Writes go through to the underlying dict.
del data.field and del data["field"] both raise AttributeError
or KeyError respectively when the key is missing, matching the
attribute and item lookup paths.
Nested payloads¶
Nested dicts and lists of dicts are converted recursively when the
serializer assembles validated_data, so attribute access carries
through nested structures.
order = serializer.validated_data
order.customer.email # nested dict
order.items[0].sku # list of nested dicts
Existing ValidatedData instances in the tree are left in place.
JSON output¶
to_json(**opts) renders the payload as a JSON string. The keyword
arguments are forwarded straight to json.dumps. A custom default
callable is composed with the restflow fallback so a payload with
Decimal, datetime, date, time, and UUID values renders
without writing a custom encoder.
order.to_json() # default formatting
order.to_json(indent=2, sort_keys=True) # extra json.dumps kwargs
order.to_json(default=my_encoder) # my_encoder runs first; restflow handles the rest
The fallback maps:
Decimalto its string formdatetime,date,timeto ISO 8601UUIDto its string formValidatedDatato a plain dict copy
__json__() returns a plain dict. Codecs and libraries that look
up __json__ see a regular dict shape rather than the subclass.
Pickling¶
__reduce__ is implemented, so ValidatedData instances pickle
and unpickle as plain dicts wrapped back into ValidatedData on
load. The same instance is safe to hand to pickle,
copy.deepcopy, or any cache backend that needs a serialized
form.
When to reach for it¶
Serializer.validated_data is already a ValidatedData instance
in restflow, so no opt-in is needed. The shape matters most in
view code that wants attribute access for readability:
async def post(self, request):
serializer = OrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
payload = serializer.validated_data
order = await Order.objects.acreate(
customer=payload.customer,
total=payload.total,
)
return await self.aserialized_response(order, status=201)
The same code with payload["customer"] is equivalent. Pick the
form that reads better in the surrounding view.
API¶
ValidatedData ¶
Bases: dict
A dict subclass returned by Serializer.validated_data that adds attribute access and JSON helpers.
Reads
vd.name, vd["name"], vd.get("name"), **vd, dict(vd) all return the same value.
Writes
vd.name = value and vd["name"] = value are equivalent and write through to the underlying dict.
Restflow adds attribute access plus a configurable to_json so payloads with Decimal, datetime, date, time, and UUID render without a custom encoder.
to_json ¶
Render this validated payload as a JSON string. All keyword arguments are forwarded to json.dumps. A user default is composed with the restflow fallback for Decimal, date, datetime, time, and UUID.
transform_validated_data ¶
Recursively convert dicts to ValidatedData and walk lists, leaving everything else untouched.