Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Field: support schema_extra aliases (v2); add alias tests
  • Loading branch information
ravishan16 committed Oct 9, 2025
commit 322c6affe508855c767fcc58c2129fcd4ebc9e77
27 changes: 19 additions & 8 deletions sqlmodel/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ def Field(
schema_extra: Optional[Dict[str, Any]] = None,
) -> Any:
current_schema_extra = schema_extra or {}
# Extract possible alias settings from schema_extra so we can control precedence
schema_validation_alias = current_schema_extra.pop("validation_alias", None)
schema_serialization_alias = current_schema_extra.pop("serialization_alias", None)
field_info_kwargs = {
"alias": alias,
"title": title,
Expand Down Expand Up @@ -434,17 +437,25 @@ def Field(
**current_schema_extra,
}
if IS_PYDANTIC_V2:
# Add Pydantic v2 specific parameters
field_info_kwargs.update(
{
"validation_alias": validation_alias,
"serialization_alias": serialization_alias,
}
# explicit params > schema_extra > alias propagation (handled later)
effective_validation_alias = (
validation_alias
if validation_alias is not None
else schema_validation_alias
)
effective_serialization_alias = (
serialization_alias
if serialization_alias is not None
else schema_serialization_alias
)
if effective_validation_alias is not None:
field_info_kwargs["validation_alias"] = effective_validation_alias
if effective_serialization_alias is not None:
field_info_kwargs["serialization_alias"] = effective_serialization_alias
else:
if validation_alias:
if validation_alias or schema_validation_alias is not None:
raise RuntimeError("validation_alias is not supported in Pydantic v1")
if serialization_alias:
if serialization_alias or schema_serialization_alias is not None:
raise RuntimeError("serialization_alias is not supported in Pydantic v1")
field_info = FieldInfo(
default,
Expand Down
92 changes: 85 additions & 7 deletions tests/test_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pydantic import Field as PField
from sqlmodel import Field, SQLModel

from tests.conftest import needs_pydanticv2
from tests.conftest import needs_pydanticv1, needs_pydanticv2

"""
Alias tests for SQLModel and Pydantic compatibility
Expand All @@ -21,8 +21,6 @@ class SQLModelUser(SQLModel):


# Models with config (validate_by_name=True)


if VERSION.startswith("2."):

class PydanticUserWithConfig(PydanticUser):
Expand Down Expand Up @@ -138,18 +136,16 @@ class SQLModelUserV2(SQLModel):
SQLModelUserV2 = None


@needs_pydanticv1
def test_validation_alias_runtimeerror_pydantic_v1():
if VERSION.startswith("2."):
pytest.skip("Only relevant for Pydantic v1")
with pytest.raises(
RuntimeError, match="validation_alias is not supported in Pydantic v1"
):
Field(validation_alias="foo")


@needs_pydanticv1
def test_serialization_alias_runtimeerror_pydantic_v1():
if VERSION.startswith("2."):
pytest.skip("Only relevant for Pydantic v1")
with pytest.raises(
RuntimeError, match="serialization_alias is not supported in Pydantic v1"
):
Expand All @@ -176,3 +172,85 @@ def test_serialize_with_serialization_alias(
assert "firstName" not in data
assert "first_name" not in data
assert data["f_name"] == "Jane"


@needs_pydanticv2
def test_schema_extra_validation_alias_sqlmodel_v2():
class M(SQLModel):
f: str = Field(schema_extra={"validation_alias": "f_alias"})

m = M.model_validate({"f_alias": "asd"})
assert m.f == "asd"


@needs_pydanticv2
def test_schema_extra_serialization_alias_sqlmodel_v2():
class M(SQLModel):
f: str = Field(schema_extra={"serialization_alias": "f_out"})

m = M(f="x")
data = m.model_dump(by_alias=True)
assert "f_out" in data
assert "f" not in data
assert data["f_out"] == "x"


@needs_pydanticv1
def test_schema_extra_validation_alias_runtimeerror_pydantic_v1():
with pytest.raises(
RuntimeError, match="validation_alias is not supported in Pydantic v1"
):
Field(schema_extra={"validation_alias": "x"})


@needs_pydanticv1
def test_schema_extra_serialization_alias_runtimeerror_pydantic_v1():
with pytest.raises(
RuntimeError, match="serialization_alias is not supported in Pydantic v1"
):
Field(schema_extra={"serialization_alias": "y"})


@needs_pydanticv2
def test_alias_plus_validation_alias_prefers_validation_alias_sqlmodel_v2():
class M(SQLModel):
first_name: str = Field(alias="fullName", validation_alias="v_name")

m = M.model_validate({"fullName": "A", "v_name": "B"})
assert m.first_name == "B"


@needs_pydanticv2
def test_alias_plus_serialization_alias_prefers_serialization_alias_sqlmodel_v2():
class M(SQLModel):
first_name: str = Field(alias="fullName", serialization_alias="f_name")

m = M(first_name="Z")
data = m.model_dump(by_alias=True)
assert "f_name" in data
assert "fullName" not in data
assert data["f_name"] == "Z"


@needs_pydanticv2
def test_alias_generator_works_sqlmodel_v2():
class M(SQLModel):
model_config = {"alias_generator": lambda s: "gen_" + s}
f: str = Field()

m = M.model_validate({"gen_f": "ok"})
assert m.f == "ok"
data = m.model_dump(by_alias=True)
assert "gen_f" in data and data["gen_f"] == "ok"


@needs_pydanticv2
def test_alias_generator_with_explicit_alias_prefers_field_alias_sqlmodel_v2():
class M(SQLModel):
model_config = {"alias_generator": lambda s: "gen_" + s}
f: str = Field(alias="custom")

m = M.model_validate({"custom": "ok"})
assert m.f == "ok"
data = m.model_dump(by_alias=True)
assert "custom" in data and "gen_f" not in data