Schema migrations¶
All of the following instructions affect only OpenAPI schemas and their initial validation. All of your incoming requests will still be converted into your HEAD schemas.
Please note that you only need to have a migration if it is a breaking change for your users. The scenarios below only describe "what you can do" but not "what you should do". For the "should" part, please refer to the how-to docs.
Add a field to the older version¶
from cadwyn import VersionChange, schema
from pydantic import Field
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema)
.field("foo")
.existed_as(type=list[str], info=Field(description="Foo")),
)
Remove a field from the older version¶
from cadwyn import VersionChange, schema
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).field("foo").didnt_exist,
)
Change a field in the older version¶
The following code allows to set an attribute of a field, such as a description:
from cadwyn import VersionChange, schema
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).field("foo").had(description="Foo"),
)
The following code allows to un-set an attribute of a field, as if it never existed:
from cadwyn import VersionChange, schema
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).field("foo").didnt_have("description"),
)
DEFAULTS WARNING:
If you add default
or default_factory
into the old version of a schema -- it will not manifest in code automatically. Instead, you should add both the default
or default_factory
, and then also add the default value using a request migration.
The reason for such behaviour is the way how Cadwyn works with pydantic and unfortunately this cannot be changed:
Cadwyn:
- Receives a request for API version
V
- Validates the request using the schemas from
V
- Marshalls the unmarshalled request body into a raw data structure using
BaseModel.dict
(BaseModel.model_dump
in Pydantic v2) using exclude_unset=True - Passes the request through all request migrations from
V
tolatest
- Validates the request using
latest
schemas
The part that causes the aforementioned problem is cadwyn's usage of exclude_unset=True
. Unfortunately, when we use it, all default fields do not get set, so latest
does not receive them. And if latest
does not have the same defaults (for example, if the field has no default and is required in latest
), then an error will occur. If we used exclude_unset=False
, then exclude_unset
would lose all its purpose for the users of our library so we cannot give it up. Instead, you should set all extras on step 4 in your request migrations.
Add a validator to the older version¶
from cadwyn import VersionChange, schema
from pydantic import Field, field_validator
@field_validator("foo")
def validate_foo(cls, value):
if not ":" in value:
raise TypeError
return value
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).validator(validate_foo).existed,
)
Remove a validator from the older version¶
from cadwyn import VersionChange, schema
from pydantic import Field, validator
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).validator(MySchema.validate_foo).didnt_exist,
)
Rename a schema in the older version¶
The following code allows to replace all schema name occurrences with the new one to make sure that the name is different in openapi.json:
from cadwyn import VersionChange, schema
class MyChange(VersionChange):
description = "..."
instructions_to_migrate_to_previous_version = (
schema(MySchema).had(name="OtherSchema"),
)