Mutations¶
Sync helpers¶
apply_input ¶
apply_input(
instance: Model,
data: Any,
*,
field_map: dict[str, str] | None = None,
exclude_fields: list[str] | None = None,
) -> ChangeResult
Set attributes from data onto instance without saving.
Returns a ChangeResult
describing fields whose value actually changed. Useful when a service wants to
inspect what the input would change before deciding whether to persist.
A file field is set as update_from_input writes it: None keeps the
file, and False leaves none.
create_from_input ¶
create_from_input(
model: type[ModelT],
data: Any,
*,
field_map: dict[str, str] | None = None,
exclude_fields: list[str] | None = None,
m2m: dict[str, Any] | None = None,
relations: Mapping[str, RelationSpec] | None = None,
context: Mapping[str, Any] | None = None,
) -> ChangeResult[ModelT]
Build, save(), and return a fresh instance of model.
Regular fields come from data (a dataclass, dict, or object with __dict__).
A file field is written as a ModelForm writes it, through the model
field's save_form_data: a file is stored, None leaves the field to the model, and False,
a form's value for a cleared file, stores no file.
m2m maps a many-to-many attribute to the rows it should hold — instances or
primary keys of rows that already exist — and set()s them after the save;
it creates nothing. A
ManyToManySpec
in relations does the other job: it writes the target rows from the payload,
creating or updating each, and then links them. A relation named by both is
refused, since it would be written twice and keep whichever ran last.
relations maps a relation name to the spec for its kind, and is the one map for
every kind — a reverse-FK
ChildSpec included. The
nested payload is read from data[relation] and written in the order the kind
dictates, not the order the map is spelled in (see
RelationPhase).
Keep the whole call inside the service's atomic block.
context is an opaque mapping forwarded verbatim down the tree, into the pool
of every row service a relation spec declares and of every scope callable. This
helper never reads it — it exists so per-row work downstream can see the acting
caller. A service opts in by handing on the pool it was called with,
context=kwargs. A catch-all is never handed the call's context, so a
service whose nested callables should see it names call_context and
hands it on beside the pool, as create_model's service does.
update_from_input ¶
update_from_input(
instance: ModelT,
data: Any,
*,
field_map: dict[str, str] | None = None,
exclude_fields: list[str] | None = None,
m2m: dict[str, Any] | None = None,
update_fields: bool | list[str] = True,
relations: Mapping[str, RelationSpec] | None = None,
context: Mapping[str, Any] | None = None,
) -> ChangeResult[ModelT]
Update instance with values from data, persisting only deltas.
By default (update_fields=True), the save call uses
update_fields=<changed> to write the minimal set of columns.
auto_now=True fields (e.g. updated_at) are automatically added to
that list so they are refreshed alongside the mutation, and so are a
changed ImageField's width_field and height_field, which
setting the image wrote on the instance. Pass False to
perform a full save, or an explicit list to control exactly which columns
are written (no auto-injection in that case).
A file field is written as a ModelForm writes it, through the model
field's save_form_data: a file replaces the one the row holds, None keeps it, and False,
a form's value for a cleared file, leaves no file, reported as a change to "".
An upload, or any File that is not one a row holds, is always written and reported,
even under the name of the file it replaces, since a name says nothing of the content;
the held file handed back as itself is no change.
m2m assigns many-to-many rows that already exist, and only where the set
of primary keys differs from the current one;
ManyToManySpec
in relations writes the target rows from the payload instead. A relation named
by both is refused rather than written twice.
relations maps a relation name to the spec for its kind, one map for
every kind. The nested payload from data[relation] is
reconciled with what is already there — create / update / orphan-remove per
the spec — and a relation the input omits is left untouched. Kinds are
written in the order their class dictates, so a forward foreign key is
resolved before this instance is saved and the assignment rides the same
diff and update_fields path as any other column. Keep the call inside
the service's atomic block.
A relation that was written stays readable off the returned instance. The one-row kinds resolve their row by re-querying, so the parent's cached related object is replaced with the row that was written — and dropped where the write removed it — rather than left holding pre-write values.
context is an opaque mapping forwarded verbatim into the pool of every
row service and scope callable the relation specs declare; this
helper never reads it. See
create_from_input.
Async helpers¶
acreate_from_input
async
¶
acreate_from_input(
model: type[ModelT],
data: Any,
*,
field_map: dict[str, str] | None = None,
exclude_fields: list[str] | None = None,
m2m: dict[str, Any] | None = None,
relations: Mapping[str, RelationSpec] | None = None,
context: Mapping[str, Any] | None = None,
) -> ChangeResult[ModelT]
Async sibling of
create_from_input
using asave()/aset().
aupdate_from_input
async
¶
aupdate_from_input(
instance: ModelT,
data: Any,
*,
field_map: dict[str, str] | None = None,
exclude_fields: list[str] | None = None,
m2m: dict[str, Any] | None = None,
update_fields: bool | list[str] = True,
relations: Mapping[str, RelationSpec] | None = None,
context: Mapping[str, Any] | None = None,
) -> ChangeResult[ModelT]
Async sibling of
update_from_input
using asave()/aset().