From 785270acfc930d73fcab0d44348a09117bbe6764 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Thu, 25 Sep 2025 15:53:32 -0500 Subject: [PATCH 1/8] feat: Add support for Pydantic v2 aliases --- sqlmodel/_compat.py | 9 ++- sqlmodel/main.py | 97 +++++++++++++++---------- tests/test_aliases.py | 161 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 38 deletions(-) create mode 100644 tests/test_aliases.py diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 38dd501c4a..0ed19b925b 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -221,8 +221,13 @@ def get_field_metadata(field: Any) -> Any: return FakeMetadata() def post_init_field_info(field_info: FieldInfo) -> None: - return None - + if IS_PYDANTIC_V2: + if field_info.alias and not field_info.validation_alias: + field_info.validation_alias = field_info.alias + if field_info.alias and not field_info.serialization_alias: + field_info.serialization_alias = field_info.alias + else: + field_info._validate() # type: ignore[attr-defined] # Dummy to make it importable def _calculate_keys( self: "SQLModel", diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 38c85915aa..1578b030bf 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -215,6 +215,8 @@ def Field( *, default_factory: Optional[NoArgAnyCallable] = None, alias: Optional[str] = None, + validation_alias: Optional[str] = None, + serialization_alias: Optional[str] = None, title: Optional[str] = None, description: Optional[str] = None, exclude: Union[ @@ -260,6 +262,8 @@ def Field( *, default_factory: Optional[NoArgAnyCallable] = None, alias: Optional[str] = None, + validation_alias: Optional[str] = None, + serialization_alias: Optional[str] = None, title: Optional[str] = None, description: Optional[str] = None, exclude: Union[ @@ -314,6 +318,8 @@ def Field( *, default_factory: Optional[NoArgAnyCallable] = None, alias: Optional[str] = None, + validation_alias: Optional[str] = None, + serialization_alias: Optional[str] = None, title: Optional[str] = None, description: Optional[str] = None, exclude: Union[ @@ -349,6 +355,8 @@ def Field( *, default_factory: Optional[NoArgAnyCallable] = None, alias: Optional[str] = None, + validation_alias: Optional[str] = None, + serialization_alias: Optional[str] = None, title: Optional[str] = None, description: Optional[str] = None, exclude: Union[ @@ -387,43 +395,60 @@ def Field( schema_extra: Optional[Dict[str, Any]] = None, ) -> Any: current_schema_extra = schema_extra or {} - field_info = FieldInfo( - default, - default_factory=default_factory, - alias=alias, - title=title, - description=description, - exclude=exclude, - include=include, - const=const, - gt=gt, - ge=ge, - lt=lt, - le=le, - multiple_of=multiple_of, - max_digits=max_digits, - decimal_places=decimal_places, - min_items=min_items, - max_items=max_items, - unique_items=unique_items, - min_length=min_length, - max_length=max_length, - allow_mutation=allow_mutation, - regex=regex, - discriminator=discriminator, - repr=repr, - primary_key=primary_key, - foreign_key=foreign_key, - ondelete=ondelete, - unique=unique, - nullable=nullable, - index=index, - sa_type=sa_type, - sa_column=sa_column, - sa_column_args=sa_column_args, - sa_column_kwargs=sa_column_kwargs, + field_info_kwargs = { + "alias": alias, + "validation_alias": validation_alias, + "serialization_alias": serialization_alias, + "title": title, + "description": description, + "exclude": exclude, + "include": include, + "const": const, + "gt": gt, + "ge": ge, + "lt": lt, + "le": le, + "multiple_of": multiple_of, + "max_digits": max_digits, + "decimal_places": decimal_places, + "min_items": min_items, + "max_items": max_items, + "unique_items": unique_items, + "min_length": min_length, + "max_length": max_length, + "allow_mutation": allow_mutation, + "regex": regex, + "discriminator": discriminator, + "repr": repr, + "primary_key": primary_key, + "foreign_key": foreign_key, + "ondelete": ondelete, + "unique": unique, + "nullable": nullable, + "index": index, + "sa_type": sa_type, + "sa_column": sa_column, + "sa_column_args": sa_column_args, + "sa_column_kwargs": sa_column_kwargs, **current_schema_extra, - ) + } + if IS_PYDANTIC_V2: + field_info = FieldInfo( + default, + default_factory=default_factory, + **field_info_kwargs, + ) + else: + if validation_alias: + raise RuntimeError("validation_alias is not supported in Pydantic v1") + if serialization_alias: + raise RuntimeError("serialization_alias is not supported in Pydantic v1") + field_info = FieldInfo( + default, + default_factory=default_factory, + **field_info_kwargs, + ) + post_init_field_info(field_info) return field_info diff --git a/tests/test_aliases.py b/tests/test_aliases.py new file mode 100644 index 0000000000..ffb77045b1 --- /dev/null +++ b/tests/test_aliases.py @@ -0,0 +1,161 @@ +from typing import Type, Union + +import pytest +from pydantic import VERSION, BaseModel, ValidationError +from pydantic import Field as PField +from sqlmodel import Field, SQLModel + + +# ----------------------------------------------------------------------------------- +# Models + + +class PydanticUser(BaseModel): + full_name: str = PField(alias="fullName") + + +class SQLModelUser(SQLModel): + full_name: str = Field(alias="fullName") + + +# Models with config (validate_by_name=True) + + +if VERSION.startswith("2."): + + class PydanticUserWithConfig(PydanticUser): + model_config = {"validate_by_name": True} + + class SQLModelUserWithConfig(SQLModelUser): + model_config = {"validate_by_name": True} + +else: + + class PydanticUserWithConfig(PydanticUser): + class Config: + allow_population_by_field_name = True + + class SQLModelUserWithConfig(SQLModelUser): + class Config: + allow_population_by_field_name = True + + +# ----------------------------------------------------------------------------------- +# Tests + +# Test validate by name + + +@pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) +def test_create_with_field_name(model: Union[Type[PydanticUser], Type[SQLModelUser]]): + with pytest.raises(ValidationError): + model(full_name="Alice") + + +@pytest.mark.parametrize("model", [PydanticUserWithConfig, SQLModelUserWithConfig]) +def test_create_with_field_name_with_config( + model: Union[Type[PydanticUserWithConfig], Type[SQLModelUserWithConfig]], +): + user = model(full_name="Alice") + assert user.full_name == "Alice" + + +# Test validate by alias + + +@pytest.mark.parametrize( + "model", + [PydanticUser, SQLModelUser, PydanticUserWithConfig, SQLModelUserWithConfig], +) +def test_create_with_alias( + model: Union[ + Type[PydanticUser], + Type[SQLModelUser], + Type[PydanticUserWithConfig], + Type[SQLModelUserWithConfig], + ], +): + user = model(fullName="Bob") # using alias + assert user.full_name == "Bob" + + +# Test validate by name and alias + + +@pytest.mark.parametrize("model", [PydanticUserWithConfig, SQLModelUserWithConfig]) +def test_create_with_both_prefers_alias( + model: Union[Type[PydanticUserWithConfig], Type[SQLModelUserWithConfig]], +): + user = model(full_name="IGNORED", fullName="Charlie") + assert user.full_name == "Charlie" # alias should take precedence + + +# Test serialize + + +@pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) +def test_dict_default_uses_field_names( + model: Union[Type[PydanticUser], Type[SQLModelUser]], +): + user = model(fullName="Dana") + data = user.dict() + assert "full_name" in data + assert "fullName" not in data + assert data["full_name"] == "Dana" + + +# Test serialize by alias + + +@pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) +def test_dict_default_uses_aliases( + model: Union[Type[PydanticUser], Type[SQLModelUser]], +): + user = model(fullName="Dana") + data = user.dict(by_alias=True) + assert "fullName" in data + assert "full_name" not in data + assert data["fullName"] == "Dana" + + +# Test json by alias + + +@pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) +def test_json_by_alias( + model: Union[Type[PydanticUser], Type[SQLModelUser]], +): + user = model(fullName="Frank") + json_data = user.json(by_alias=True) + assert ('"fullName":"Frank"' in json_data) or ('"fullName": "Frank"' in json_data) + assert "full_name" not in json_data + + +class PydanticUserV2(BaseModel): + first_name: str = PField( + validation_alias="firstName", serialization_alias="f_name" + ) + + +class SQLModelUserV2(SQLModel): + first_name: str = Field( + validation_alias="firstName", serialization_alias="f_name" + ) + + +@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) +def test_create_with_validation_alias(model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]]): + user = model(firstName="John") + assert user.first_name == "John" + + +@pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) +def test_serialize_with_serialization_alias( + model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]] +): + user = model(firstName="Jane") + data = user.dict(by_alias=True) + assert "f_name" in data + assert "firstName" not in data + assert "first_name" not in data + assert data["f_name"] == "Jane" \ No newline at end of file From 72e8c36b914c2aefbb8208a7d3933328663e7506 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Thu, 25 Sep 2025 16:50:13 -0500 Subject: [PATCH 2/8] fix: Skip alias tests in Pydantic v1 --- tests/test_aliases.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_aliases.py b/tests/test_aliases.py index ffb77045b1..cb59b307db 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -143,12 +143,14 @@ class SQLModelUserV2(SQLModel): ) +@pytest.mark.skipif(not VERSION.startswith("2."), reason="validation_alias and serialization_alias are not supported in Pydantic v1") @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_create_with_validation_alias(model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]]): user = model(firstName="John") assert user.first_name == "John" +@pytest.mark.skipif(not VERSION.startswith("2."), reason="validation_alias and serialization_alias are not supported in Pydantic v1") @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_serialize_with_serialization_alias( model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]] From 15c79549cf56a72c186ca5683ead32a8b94226a1 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Thu, 25 Sep 2025 17:17:28 -0500 Subject: [PATCH 3/8] feat: Add support for Pydantic v2 aliases --- sqlmodel/_compat.py | 1 + tests/test_aliases.py | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 0ed19b925b..f8bbb46e7b 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -228,6 +228,7 @@ def post_init_field_info(field_info: FieldInfo) -> None: field_info.serialization_alias = field_info.alias else: field_info._validate() # type: ignore[attr-defined] + # Dummy to make it importable def _calculate_keys( self: "SQLModel", diff --git a/tests/test_aliases.py b/tests/test_aliases.py index cb59b307db..c0d2534fdc 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -5,7 +5,6 @@ from pydantic import Field as PField from sqlmodel import Field, SQLModel - # ----------------------------------------------------------------------------------- # Models @@ -132,32 +131,36 @@ def test_json_by_alias( class PydanticUserV2(BaseModel): - first_name: str = PField( - validation_alias="firstName", serialization_alias="f_name" - ) + first_name: str = PField(validation_alias="firstName", serialization_alias="f_name") class SQLModelUserV2(SQLModel): - first_name: str = Field( - validation_alias="firstName", serialization_alias="f_name" - ) + first_name: str = Field(validation_alias="firstName", serialization_alias="f_name") -@pytest.mark.skipif(not VERSION.startswith("2."), reason="validation_alias and serialization_alias are not supported in Pydantic v1") +@pytest.mark.skipif( + not VERSION.startswith("2."), + reason="validation_alias and serialization_alias are not supported in Pydantic v1", +) @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) -def test_create_with_validation_alias(model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]]): +def test_create_with_validation_alias( + model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], +): user = model(firstName="John") assert user.first_name == "John" -@pytest.mark.skipif(not VERSION.startswith("2."), reason="validation_alias and serialization_alias are not supported in Pydantic v1") +@pytest.mark.skipif( + not VERSION.startswith("2."), + reason="validation_alias and serialization_alias are not supported in Pydantic v1", +) @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_serialize_with_serialization_alias( - model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]] + model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], ): user = model(firstName="Jane") data = user.dict(by_alias=True) assert "f_name" in data assert "firstName" not in data assert "first_name" not in data - assert data["f_name"] == "Jane" \ No newline at end of file + assert data["f_name"] == "Jane" From 3cc2b1e3e616b9ca8648d4dcc91b1f83bc566496 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Thu, 25 Sep 2025 17:27:52 -0500 Subject: [PATCH 4/8] fix: Skip alias tests in Pydantic v1 --- tests/test_aliases.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/test_aliases.py b/tests/test_aliases.py index c0d2534fdc..743d74db7f 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -97,7 +97,7 @@ def test_dict_default_uses_field_names( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.dict() + data = user.model_dump() assert "full_name" in data assert "fullName" not in data assert data["full_name"] == "Dana" @@ -111,7 +111,7 @@ def test_dict_default_uses_aliases( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.dict(by_alias=True) + data = user.model_dump(by_alias=True) assert "fullName" in data assert "full_name" not in data assert data["fullName"] == "Dana" @@ -125,17 +125,27 @@ def test_json_by_alias( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Frank") - json_data = user.json(by_alias=True) + json_data = user.model_dump_json(by_alias=True) assert ('"fullName":"Frank"' in json_data) or ('"fullName": "Frank"' in json_data) assert "full_name" not in json_data -class PydanticUserV2(BaseModel): - first_name: str = PField(validation_alias="firstName", serialization_alias="f_name") +# Pydantic v2 specific models - only define if we're running Pydantic v2 +if VERSION.startswith("2."): + class PydanticUserV2(BaseModel): + first_name: str = PField( + validation_alias="firstName", serialization_alias="f_name" + ) -class SQLModelUserV2(SQLModel): - first_name: str = Field(validation_alias="firstName", serialization_alias="f_name") + class SQLModelUserV2(SQLModel): + first_name: str = Field( + validation_alias="firstName", serialization_alias="f_name" + ) +else: + # Dummy classes for Pydantic v1 to prevent import errors + PydanticUserV2 = None + SQLModelUserV2 = None @pytest.mark.skipif( @@ -159,7 +169,7 @@ def test_serialize_with_serialization_alias( model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], ): user = model(firstName="Jane") - data = user.dict(by_alias=True) + data = user.model_dump(by_alias=True) assert "f_name" in data assert "firstName" not in data assert "first_name" not in data From 34d2a4500e26a62f7b9e30982eebd44538e08859 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Thu, 25 Sep 2025 17:39:06 -0500 Subject: [PATCH 5/8] fix: Add Pydantic v2 alias support with backward compatibility This commit fixes Field(alias="...") functionality in Pydantic v2 while maintaining full backward compatibility with Pydantic v1. --- sqlmodel/main.py | 9 +++++++-- tests/test_aliases.py | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 1578b030bf..8866ac7282 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -397,8 +397,6 @@ def Field( current_schema_extra = schema_extra or {} field_info_kwargs = { "alias": alias, - "validation_alias": validation_alias, - "serialization_alias": serialization_alias, "title": title, "description": description, "exclude": exclude, @@ -433,6 +431,13 @@ 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, + } + ) field_info = FieldInfo( default, default_factory=default_factory, diff --git a/tests/test_aliases.py b/tests/test_aliases.py index 743d74db7f..f384abf010 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -97,7 +97,7 @@ def test_dict_default_uses_field_names( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.model_dump() + data = user.dict() assert "full_name" in data assert "fullName" not in data assert data["full_name"] == "Dana" @@ -111,7 +111,7 @@ def test_dict_default_uses_aliases( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.model_dump(by_alias=True) + data = user.dict(by_alias=True) assert "fullName" in data assert "full_name" not in data assert data["fullName"] == "Dana" @@ -125,7 +125,7 @@ def test_json_by_alias( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Frank") - json_data = user.model_dump_json(by_alias=True) + json_data = user.json(by_alias=True) assert ('"fullName":"Frank"' in json_data) or ('"fullName": "Frank"' in json_data) assert "full_name" not in json_data @@ -169,7 +169,7 @@ def test_serialize_with_serialization_alias( model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], ): user = model(firstName="Jane") - data = user.model_dump(by_alias=True) + data = user.dict(by_alias=True) assert "f_name" in data assert "firstName" not in data assert "first_name" not in data From 66671099e762db0972814030d246d6d13695c7f2 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Tue, 30 Sep 2025 16:49:54 -0500 Subject: [PATCH 6/8] Apply suggestion from @YuriiMotov Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> --- tests/test_aliases.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_aliases.py b/tests/test_aliases.py index f384abf010..eca244b122 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -148,10 +148,7 @@ class SQLModelUserV2(SQLModel): SQLModelUserV2 = None -@pytest.mark.skipif( - not VERSION.startswith("2."), - reason="validation_alias and serialization_alias are not supported in Pydantic v1", -) +@needs_pydanticv2 @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_create_with_validation_alias( model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], From 382a52da35ac74a7c57726cb72ae4f37bbd2c1f8 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Tue, 30 Sep 2025 17:03:54 -0500 Subject: [PATCH 7/8] fix: move this outside of if..else.. block to avoid duplicati as per Yurii's comment https://github.com/fastapi/sqlmodel/pull/1577/files/34d2a4500e26a62f7b9e30982eebd44538e08859\#r2392422553 --- sqlmodel/main.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 8866ac7282..e9b732a369 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -438,21 +438,16 @@ def Field( "serialization_alias": serialization_alias, } ) - field_info = FieldInfo( - default, - default_factory=default_factory, - **field_info_kwargs, - ) else: if validation_alias: raise RuntimeError("validation_alias is not supported in Pydantic v1") if serialization_alias: raise RuntimeError("serialization_alias is not supported in Pydantic v1") - field_info = FieldInfo( - default, - default_factory=default_factory, - **field_info_kwargs, - ) + field_info = FieldInfo( + default, + default_factory=default_factory, + **field_info_kwargs, + ) post_init_field_info(field_info) return field_info From d3e761ebc5c965d39c60b7510264c83e29d6d422 Mon Sep 17 00:00:00 2001 From: Ravishankar Sivasubramaniam Date: Tue, 30 Sep 2025 18:28:13 -0500 Subject: [PATCH 8/8] fix: Addressed yurii's comments --- tests/test_aliases.py | 69 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/tests/test_aliases.py b/tests/test_aliases.py index eca244b122..ea32002a95 100644 --- a/tests/test_aliases.py +++ b/tests/test_aliases.py @@ -5,8 +5,11 @@ from pydantic import Field as PField from sqlmodel import Field, SQLModel -# ----------------------------------------------------------------------------------- -# Models +from tests.conftest import needs_pydanticv2 + +""" +Alias tests for SQLModel and Pydantic compatibility +""" class PydanticUser(BaseModel): @@ -39,12 +42,6 @@ class Config: allow_population_by_field_name = True -# ----------------------------------------------------------------------------------- -# Tests - -# Test validate by name - - @pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) def test_create_with_field_name(model: Union[Type[PydanticUser], Type[SQLModelUser]]): with pytest.raises(ValidationError): @@ -59,9 +56,6 @@ def test_create_with_field_name_with_config( assert user.full_name == "Alice" -# Test validate by alias - - @pytest.mark.parametrize( "model", [PydanticUser, SQLModelUser, PydanticUserWithConfig, SQLModelUserWithConfig], @@ -78,9 +72,6 @@ def test_create_with_alias( assert user.full_name == "Bob" -# Test validate by name and alias - - @pytest.mark.parametrize("model", [PydanticUserWithConfig, SQLModelUserWithConfig]) def test_create_with_both_prefers_alias( model: Union[Type[PydanticUserWithConfig], Type[SQLModelUserWithConfig]], @@ -89,48 +80,47 @@ def test_create_with_both_prefers_alias( assert user.full_name == "Charlie" # alias should take precedence -# Test serialize - - @pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) def test_dict_default_uses_field_names( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.dict() + if VERSION.startswith("2."): + data = user.model_dump() + else: + data = user.dict() assert "full_name" in data assert "fullName" not in data assert data["full_name"] == "Dana" -# Test serialize by alias - - @pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) def test_dict_default_uses_aliases( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Dana") - data = user.dict(by_alias=True) + if VERSION.startswith("2."): + data = user.model_dump(by_alias=True) + else: + data = user.dict(by_alias=True) assert "fullName" in data assert "full_name" not in data assert data["fullName"] == "Dana" -# Test json by alias - - @pytest.mark.parametrize("model", [PydanticUser, SQLModelUser]) def test_json_by_alias( model: Union[Type[PydanticUser], Type[SQLModelUser]], ): user = model(fullName="Frank") - json_data = user.json(by_alias=True) + if VERSION.startswith("2."): + json_data = user.model_dump_json(by_alias=True) + else: + json_data = user.json(by_alias=True) assert ('"fullName":"Frank"' in json_data) or ('"fullName": "Frank"' in json_data) assert "full_name" not in json_data -# Pydantic v2 specific models - only define if we're running Pydantic v2 if VERSION.startswith("2."): class PydanticUserV2(BaseModel): @@ -148,6 +138,24 @@ class SQLModelUserV2(SQLModel): SQLModelUserV2 = None +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") + + +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" + ): + Field(serialization_alias="bar") + + @needs_pydanticv2 @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_create_with_validation_alias( @@ -157,16 +165,13 @@ def test_create_with_validation_alias( assert user.first_name == "John" -@pytest.mark.skipif( - not VERSION.startswith("2."), - reason="validation_alias and serialization_alias are not supported in Pydantic v1", -) +@needs_pydanticv2 @pytest.mark.parametrize("model", [PydanticUserV2, SQLModelUserV2]) def test_serialize_with_serialization_alias( model: Union[Type[PydanticUserV2], Type[SQLModelUserV2]], ): user = model(firstName="Jane") - data = user.dict(by_alias=True) + data = user.model_dump(by_alias=True) assert "f_name" in data assert "firstName" not in data assert "first_name" not in data