Serialization
When building APIs you often need to convert models and their relationships to dicts or JSON. Articulate includes methods for those conversions, plus controls over which attributes appear in the serialized output.
Serializing models and collections
Section titled “Serializing models and collections”Serializing to a dict
Section titled “Serializing to a dict”to_dict() converts a model and its loaded relations. It recurses, so relations of relations are converted too:
user = await User.query().with_("roles").first()
user.to_dict()To convert only the attributes, skipping relations, use attributes_to_dict(). The mirror image, relations_to_dict(), returns just the loaded relations:
user.attributes_to_dict()user.relations_to_dict()Collections convert the same way:
users = await User.all()
users.to_dict() # a list of dictsSerializing to JSON
Section titled “Serializing to JSON”to_json() returns a JSON string. Extra keyword arguments are passed through to json.dumps, so formatting options work as usual:
user.to_json()user.to_json(indent=2)Hiding attributes
Section titled “Hiding attributes”To keep an attribute such as a password hash out of the serialized output, list it in hidden:
class User(Model): hidden = ("password",)To hide a relation, name the relation in hidden as well.
Alternatively, visible is an allowlist. When it is set, only the attributes it names are serialized:
class User(Model): visible = ("first_name", "last_name")Both apply to attributes and relations.
Changing visibility for one model
Section titled “Changing visibility for one model”Each of these affects only the model you call it on — never the class, and never other instances:
user.make_visible("password").to_dict() # reveal a normally hidden attributeuser.merge_visible(["name", "email"]).to_dict()
user.make_hidden("email").to_dict() # hide more attributesuser.merge_hidden(["name", "email"]).to_dict()
user.set_visible(["id", "name"]).to_dict() # replace the allowlist outrightuser.set_hidden(["email", "password"]).to_dict()They all return the model, so they chain. get_hidden() and get_visible() report what is currently in effect.
On a collection, the same four helpers apply to every model at once:
users.make_hidden("email")users.set_visible(["id", "name"])Appending values
Section titled “Appending values”Sometimes you want to serialize a value that has no column. Define the accessor first:
from almasix.orm import Attribute, Model
class User(Model): is_admin = Attribute(get=lambda _value, attributes: attributes["role"] == "admin")Then add its name to appends so it is always included:
class User(Model): appends = ("is_admin",)Appended attributes respect visible and hidden, exactly as columns do.
Appending at runtime
Section titled “Appending at runtime”user.append("is_admin").to_dict()user.merge_appends(["is_admin", "status"]).to_dict()user.set_appends(["is_admin"]).to_dict() # replace the listuser.without_appends().to_dict() # drop every appended keyget_appends() reports the appended keys currently in effect, and users.append("is_admin") applies to a whole collection.
Date serialization
Section titled “Date serialization”Dates serialize to ISO-8601 by default. To change that for every date on a model, set date_format:
class User(Model): date_format = "%Y-%m-%d"For full control, override serialize_date. It affects serialization only, never how values are stored:
class User(Model): def serialize_date(self, value) -> str: return value.strftime("%d %b %Y")To set the format for a single attribute, put it on the cast — see Mutators & Casts:
class User(Model): casts = {"birthday": "date:%Y-%m-%d", "joined_at": "datetime:%Y-%m-%d %H:00"}When to reach for a resource instead
Section titled “When to reach for a resource instead”hidden / visible / appends shape a model everywhere it is serialized —
in an API response, a queue payload, a log line. When only the API should look
different, or the shape depends on who is asking, that belongs in an
API Resource rather than on the model.
Related
Section titled “Related”- API Resources — the transformation layer for API output
- Mutators & Casts — accessors and the cast that shapes a value
- Collections — the collection methods behind
to_dict() - Articulate: Getting Started — models and retrieval