Skip to content

Commit 63578da

Browse files
committed
Allow AliasChoices and AliasPath for validation_alias in Field
1 parent 5611bda commit 63578da

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

sqlmodel/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@
114114
from sqlalchemy.types import Uuid as Uuid
115115

116116
# From SQLModel, modifications of SQLAlchemy or equivalents of Pydantic
117+
from .main import AliasChoices as AliasChoices
118+
from .main import AliasPath as AliasPath
117119
from .main import Field as Field
118120
from .main import Relationship as Relationship
119121
from .main import SQLModel as SQLModel

sqlmodel/main.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
overload,
2323
)
2424

25+
from pydantic import AliasChoices as AliasChoices
26+
from pydantic import AliasPath as AliasPath
2527
from pydantic import BaseModel, EmailStr
2628
from pydantic.fields import FieldInfo as PydanticFieldInfo
2729
from sqlalchemy import (
@@ -207,7 +209,7 @@ def Field(
207209
*,
208210
default_factory: Optional[NoArgAnyCallable] = None,
209211
alias: Optional[str] = None,
210-
validation_alias: Optional[str] = None,
212+
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
211213
serialization_alias: Optional[str] = None,
212214
title: Optional[str] = None,
213215
description: Optional[str] = None,
@@ -250,7 +252,7 @@ def Field(
250252
*,
251253
default_factory: Optional[NoArgAnyCallable] = None,
252254
alias: Optional[str] = None,
253-
validation_alias: Optional[str] = None,
255+
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
254256
serialization_alias: Optional[str] = None,
255257
title: Optional[str] = None,
256258
description: Optional[str] = None,
@@ -302,7 +304,7 @@ def Field(
302304
*,
303305
default_factory: Optional[NoArgAnyCallable] = None,
304306
alias: Optional[str] = None,
305-
validation_alias: Optional[str] = None,
307+
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
306308
serialization_alias: Optional[str] = None,
307309
title: Optional[str] = None,
308310
description: Optional[str] = None,
@@ -335,7 +337,7 @@ def Field(
335337
*,
336338
default_factory: Optional[NoArgAnyCallable] = None,
337339
alias: Optional[str] = None,
338-
validation_alias: Optional[str] = None,
340+
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
339341
serialization_alias: Optional[str] = None,
340342
title: Optional[str] = None,
341343
description: Optional[str] = None,

tests/test_aliases.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
from typing import Union
1+
from typing import Optional, Union
22

33
import pytest
4+
from pydantic import AliasChoices as PAliasChoices
5+
from pydantic import AliasPath as PAliasPath
46
from pydantic import BaseModel, ValidationError
57
from pydantic import Field as PField
6-
from sqlmodel import Field, SQLModel
8+
from sqlmodel import AliasChoices, AliasPath, Field, SQLModel
79

810
"""
911
Alias tests for SQLModel and Pydantic compatibility
@@ -99,10 +101,22 @@ def test_json_by_alias(
99101

100102
class PydanticUserV2(BaseModel):
101103
first_name: str = PField(validation_alias="firstName", serialization_alias="f_name")
104+
second_name: Optional[str] = PField(
105+
default=None, validation_alias=PAliasChoices("secondName", "surname")
106+
)
107+
nickname: Optional[str] = PField(
108+
default=None, validation_alias=PAliasPath("names", 2)
109+
)
102110

103111

104112
class SQLModelUserV2(SQLModel):
105113
first_name: str = Field(validation_alias="firstName", serialization_alias="f_name")
114+
second_name: Optional[str] = Field(
115+
default=None, validation_alias=AliasChoices("secondName", "surname")
116+
)
117+
nickname: Optional[str] = Field(
118+
default=None, validation_alias=AliasPath("names", 2)
119+
)
106120

107121

108122
@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2])
@@ -113,6 +127,27 @@ def test_create_with_validation_alias(
113127
assert user.first_name == "John"
114128

115129

130+
@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2])
131+
def test_create_with_validation_alias_alias_choices(
132+
model: Union[type[PydanticUserV2], type[SQLModelUserV2]],
133+
):
134+
user = model.model_validate({"firstName": "John", "secondName": "Doe"})
135+
assert user.second_name == "Doe"
136+
137+
user2 = model.model_validate({"firstName": "John", "surname": "Doe"})
138+
assert user2.second_name == "Doe"
139+
140+
141+
@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2])
142+
def test_create_with_validation_alias_alias_path(
143+
model: Union[type[PydanticUserV2], type[SQLModelUserV2]],
144+
):
145+
user = model.model_validate(
146+
{"firstName": "John", "names": ["John", "Doe", "Johnny"]}
147+
)
148+
assert user.nickname == "Johnny"
149+
150+
116151
@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2])
117152
def test_serialize_with_serialization_alias(
118153
model: Union[type[PydanticUserV2], type[SQLModelUserV2]],

0 commit comments

Comments
 (0)