Schemas API#
fastapi_restly.schemas implements the Pydantic schema layer: schema base
classes, the ReadOnly and WriteOnly field markers, relationship
reference types, and schema generation from SQLAlchemy models.
See also
Work with Foreign Keys and Relationships covers MustExist checked foreign
keys and the IDRef / IDSchema relationship wire contract.
Custom Schemas and Field Types describes schema bases, field markers, and
aliases in prose.
- class fastapi_restly.schemas.BaseSchema#
Bases:
BaseModelThin Pydantic base for ORM-facing Restly schemas.
Equivalent to:
class BaseSchema(pydantic.BaseModel): model_config = pydantic.ConfigDict(from_attributes=True)
from_attributes=Truelets Pydantic/FastAPI validate objects by attribute when the schema is used directly. Generated Restly routes still serialize throughto_response_schema()so Restly-specific behavior such asWriteOnlyfiltering and relationship-id normalization is applied.- model_config: ClassVar[ConfigDict] = {'from_attributes': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class fastapi_restly.schemas.IDRef(value: ~typing.Any = <object object>, *, id: ~typing.Annotated[~typing.Any, fr.ReadOnly])#
Bases:
IDSchema,Generic[SQLAlchemyModel]Flat-id reference to a related row, for a RELATIONSHIP-named field.
Use
IDRef[T]when the field is named after a relationship (author: IDRef[User],products: list[IDRef[Product]]), not after a*_idcolumn. The wire format is the raw id (5); input also accepts{"id": N}. Restly resolves the id to the related ORM object, sodata.authoris anIDRefwrapper (read.id), not a plain scalar.For a scalar foreign-key COLUMN (
author_id,task_id) usefr.MustExist[int, T]instead – it keeps the field a plain checked id (data.author_id == 1), rather than making a*_idfield an object wrapper. For nested{"id": N}wire on a relationship, useIDSchema[T].author: IDRef[User] # to-one relationship, flat id products: list[IDRef[Product]] # serializes as [1, 2, 3]
Resolution is an UNSCOPED existence check: the row is fetched by primary key only, with no view
build_queryscoping (tenant, soft-delete, row-level visibility), so a reference to a row the caller cannot otherwise see still resolves. Gate visibility inauthorize(data.<field>.idis the requested id, before resolution) orbefore_commit(the resolved row is on the built object). See the Foreign Keys and Relationships how-to, “Visibility and Multi-Tenancy”.- model_config: ClassVar[ConfigDict] = {'from_attributes': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class fastapi_restly.schemas.IDSchema(*, id: Annotated[Any, fr.ReadOnly])#
Bases:
BaseSchema,Generic[SQLAlchemyModel]Response-schema base that adds a read-only
id; parametrized as a field type, a nested-object relationship reference.As a BASE CLASS (
class UserRead(IDSchema): ...) it adds the resource’s own read-onlyid– the common use.As a FIELD TYPE on a RELATIONSHIP-named field (
author: IDSchema[User]) the wire format is{"id": N}(JSON-API / React-Admin); Restly resolves the id to the related ORM object, sodata.authoris a wrapper (read.id). UseIDRef[User]for flat-id wire (5) instead.
For a scalar foreign-key COLUMN (
author_id), usefr.MustExist[int, T]rather thanIDSchema/IDRef: it keeps the field a plain checked id instead of turning a*_idfield into an object wrapper.- get_sql_model_annotation() type[SQLAlchemyModel] | None#
Return the annotation on IDSchema when used as:
foo: IDSchema[Foo]
This property will return “Foo”.
- id: ReadOnly, FieldInfo(annotation=NoneType, required=True, json_schema_extra={'readOnly': True})]#
- model_config: ClassVar[ConfigDict] = {'from_attributes': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class fastapi_restly.schemas.MustExist#
Bases:
objectExistence-checked scalar foreign key.
MustExist[int]is a checkedintforeign key; the target model is inferred from the column’sForeignKey. Spell the model out with a second argument when you want it explicit (MustExist[int, Post]), and use the first for a non-int primary key (MustExist[UUID, Account]).Unlike
IDRef/IDSchemathis is not a wrapper: the field stays the pk scalar everywhere (wire, column,data.<field>), plus a batched existence check on write (404 on a miss). It is exactly the marker formAnnotated[<pk>, RefExists(Model)]– use that directly if you prefer.
- class fastapi_restly.schemas.TimestampsSchemaMixin(*, created_at: Annotated[datetime, fr.ReadOnly], updated_at: Annotated[datetime, fr.ReadOnly])#
Bases:
BaseModel- created_at: ReadOnly, FieldInfo(annotation=NoneType, required=True, json_schema_extra={'readOnly': True})]#
- model_config: ClassVar[ConfigDict] = {}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- updated_at: ReadOnly, FieldInfo(annotation=NoneType, required=True, json_schema_extra={'readOnly': True})]#
- fastapi_restly.schemas.create_schema_from_model(model_cls: type[DeclarativeBase], *, schema_name: str | None = None, include_relationships: bool = True, include_readonly_fields: bool = True) type[BaseSchema]#
Auto-generate a Pydantic schema from a SQLAlchemy model.
- Args:
model_cls: The SQLAlchemy model class schema_name: Optional name for the generated schema class include_relationships: Whether to include relationship fields include_readonly_fields: Whether to include read-only fields like id, created_at, etc.
- Returns:
A Pydantic schema class