import builtins
import ipaddress
import uuid
+import warnings
import weakref
- from collections.abc import Mapping, Sequence, Set
+ from collections.abc import Callable, Mapping, Sequence, Set
+ from dataclasses import dataclass
from datetime import date, datetime, time, timedelta
from decimal import Decimal
from enum import Enum
def Field(
default: Any = Undefined,
*,
- default_factory: Optional[NoArgAnyCallable] = None,
- alias: Optional[str] = None,
- validation_alias: Optional[str] = None,
- serialization_alias: Optional[str] = None,
- title: Optional[str] = None,
- field_title_generator: Optional[Callable[[str, PydanticFieldInfo], str]] = None,
- description: Optional[str] = None,
- examples: Optional[list[Any]] = None,
- deprecated: Union[Deprecated, str, bool, None] = None,
- exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- exclude_if: Optional[Callable[[Any], bool]] = None,
- include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- const: Optional[bool] = None,
- gt: Optional[float] = None,
- ge: Optional[float] = None,
- lt: Optional[float] = None,
- le: Optional[float] = None,
- multiple_of: Optional[float] = None,
- max_digits: Optional[int] = None,
- decimal_places: Optional[int] = None,
- min_items: Optional[int] = None,
- max_items: Optional[int] = None,
- unique_items: Optional[bool] = None,
- min_length: Optional[int] = None,
- max_length: Optional[int] = None,
+ default_factory: NoArgAnyCallable | None = None,
+ alias: str | None = None,
+ validation_alias: str | None = None,
+ serialization_alias: str | None = None,
+ title: str | None = None,
++ field_title_generator: Callable[[str, PydanticFieldInfo], str] | None = None,
+ description: str | None = None,
++ examples: list[Any] | None = None,
++ deprecated: Deprecated | str | bool | None = None,
+ exclude: Set[int | str] | Mapping[int | str, Any] | Any = None,
++ exclude_if: Callable[[Any], bool] | None = None,
+ include: Set[int | str] | Mapping[int | str, Any] | Any = None,
+ const: bool | None = None,
+ gt: float | None = None,
+ ge: float | None = None,
+ lt: float | None = None,
+ le: float | None = None,
+ multiple_of: float | None = None,
+ max_digits: int | None = None,
+ decimal_places: int | None = None,
+ min_items: int | None = None,
+ max_items: int | None = None,
+ unique_items: bool | None = None,
+ min_length: int | None = None,
+ max_length: int | None = None,
allow_mutation: bool = True,
- regex: Optional[str] = None,
- strict: Optional[bool] = None,
- discriminator: Optional[str] = None,
+ regex: str | None = None,
++ strict: bool | None = None,
+ discriminator: str | None = None,
repr: bool = True,
- primary_key: Union[bool, UndefinedType] = Undefined,
+ primary_key: bool | UndefinedType = Undefined,
foreign_key: Any = Undefined,
- unique: Union[bool, UndefinedType] = Undefined,
- nullable: Union[bool, UndefinedType] = Undefined,
- index: Union[bool, UndefinedType] = Undefined,
- sa_type: Union[type[Any], UndefinedType] = Undefined,
- sa_column_args: Union[Sequence[Any], UndefinedType] = Undefined,
- sa_column_kwargs: Union[Mapping[str, Any], UndefinedType] = Undefined,
- schema_extra: Optional[dict[str, Any]] = None,
+ unique: bool | UndefinedType = Undefined,
+ nullable: bool | UndefinedType = Undefined,
+ index: bool | UndefinedType = Undefined,
+ sa_type: type[Any] | UndefinedType = Undefined,
+ sa_column_args: Sequence[Any] | UndefinedType = Undefined,
+ sa_column_kwargs: Mapping[str, Any] | UndefinedType = Undefined,
+ schema_extra: dict[str, Any] | None = None,
) -> Any: ...
def Field(
default: Any = Undefined,
*,
- default_factory: Optional[NoArgAnyCallable] = None,
- alias: Optional[str] = None,
- validation_alias: Optional[str] = None,
- serialization_alias: Optional[str] = None,
- title: Optional[str] = None,
- field_title_generator: Optional[Callable[[str, PydanticFieldInfo], str]] = None,
- description: Optional[str] = None,
- examples: Optional[list[Any]] = None,
- deprecated: Union[Deprecated, str, bool, None] = None,
- exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- exclude_if: Optional[Callable[[Any], bool]] = None,
- include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- const: Optional[bool] = None,
- gt: Optional[float] = None,
- ge: Optional[float] = None,
- lt: Optional[float] = None,
- le: Optional[float] = None,
- multiple_of: Optional[float] = None,
- max_digits: Optional[int] = None,
- decimal_places: Optional[int] = None,
- min_items: Optional[int] = None,
- max_items: Optional[int] = None,
- unique_items: Optional[bool] = None,
- min_length: Optional[int] = None,
- max_length: Optional[int] = None,
+ default_factory: NoArgAnyCallable | None = None,
+ alias: str | None = None,
+ validation_alias: str | None = None,
+ serialization_alias: str | None = None,
+ title: str | None = None,
++ field_title_generator: Callable[[str, PydanticFieldInfo], str] | None = None,
+ description: str | None = None,
++ examples: list[Any] | None = None,
++ deprecated: Deprecated | str | bool | None = None,
+ exclude: Set[int | str] | Mapping[int | str, Any] | Any = None,
++ exclude_if: Callable[[Any], bool] | None = None,
+ include: Set[int | str] | Mapping[int | str, Any] | Any = None,
+ const: bool | None = None,
+ gt: float | None = None,
+ ge: float | None = None,
+ lt: float | None = None,
+ le: float | None = None,
+ multiple_of: float | None = None,
+ max_digits: int | None = None,
+ decimal_places: int | None = None,
+ min_items: int | None = None,
+ max_items: int | None = None,
+ unique_items: bool | None = None,
+ min_length: int | None = None,
+ max_length: int | None = None,
allow_mutation: bool = True,
- regex: Optional[str] = None,
- strict: Optional[bool] = None,
- discriminator: Optional[str] = None,
+ regex: str | None = None,
++ strict: bool | None = None,
+ discriminator: str | None = None,
repr: bool = True,
- primary_key: Union[bool, UndefinedType] = Undefined,
+ primary_key: bool | UndefinedType = Undefined,
foreign_key: str,
- ondelete: Union[OnDeleteType, UndefinedType] = Undefined,
- unique: Union[bool, UndefinedType] = Undefined,
- nullable: Union[bool, UndefinedType] = Undefined,
- index: Union[bool, UndefinedType] = Undefined,
- sa_type: Union[type[Any], UndefinedType] = Undefined,
- sa_column_args: Union[Sequence[Any], UndefinedType] = Undefined,
- sa_column_kwargs: Union[Mapping[str, Any], UndefinedType] = Undefined,
- schema_extra: Optional[dict[str, Any]] = None,
+ ondelete: OnDeleteType | UndefinedType = Undefined,
+ unique: bool | UndefinedType = Undefined,
+ nullable: bool | UndefinedType = Undefined,
+ index: bool | UndefinedType = Undefined,
+ sa_type: type[Any] | UndefinedType = Undefined,
+ sa_column_args: Sequence[Any] | UndefinedType = Undefined,
+ sa_column_kwargs: Mapping[str, Any] | UndefinedType = Undefined,
+ schema_extra: dict[str, Any] | None = None,
) -> Any: ...
def Field(
default: Any = Undefined,
*,
- default_factory: Optional[NoArgAnyCallable] = None,
- alias: Optional[str] = None,
- validation_alias: Optional[str] = None,
- serialization_alias: Optional[str] = None,
- title: Optional[str] = None,
- field_title_generator: Optional[Callable[[str, PydanticFieldInfo], str]] = None,
- description: Optional[str] = None,
- examples: Optional[list[Any]] = None,
- deprecated: Union[Deprecated, str, bool, None] = None,
- exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- exclude_if: Optional[Callable[[Any], bool]] = None,
- include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- const: Optional[bool] = None,
- gt: Optional[float] = None,
- ge: Optional[float] = None,
- lt: Optional[float] = None,
- le: Optional[float] = None,
- multiple_of: Optional[float] = None,
- max_digits: Optional[int] = None,
- decimal_places: Optional[int] = None,
- min_items: Optional[int] = None,
- max_items: Optional[int] = None,
- unique_items: Optional[bool] = None,
- min_length: Optional[int] = None,
- max_length: Optional[int] = None,
+ default_factory: NoArgAnyCallable | None = None,
+ alias: str | None = None,
+ validation_alias: str | None = None,
+ serialization_alias: str | None = None,
+ title: str | None = None,
++ field_title_generator: Callable[[str, PydanticFieldInfo], str] | None = None,
+ description: str | None = None,
++ examples: list[Any] | None = None,
++ deprecated: Deprecated | str | bool | None = None,
+ exclude: Set[int | str] | Mapping[int | str, Any] | Any = None,
++ exclude_if: Callable[[Any], bool] | None = None,
+ include: Set[int | str] | Mapping[int | str, Any] | Any = None,
+ const: bool | None = None,
+ gt: float | None = None,
+ ge: float | None = None,
+ lt: float | None = None,
+ le: float | None = None,
+ multiple_of: float | None = None,
+ max_digits: int | None = None,
+ decimal_places: int | None = None,
+ min_items: int | None = None,
+ max_items: int | None = None,
+ unique_items: bool | None = None,
+ min_length: int | None = None,
+ max_length: int | None = None,
allow_mutation: bool = True,
- regex: Optional[str] = None,
- strict: Optional[bool] = None,
- discriminator: Optional[str] = None,
+ regex: str | None = None,
++ strict: bool | None = None,
+ discriminator: str | None = None,
repr: bool = True,
- sa_column: Union[Column[Any], UndefinedType] = Undefined,
- schema_extra: Optional[dict[str, Any]] = None,
+ sa_column: Column[Any] | UndefinedType = Undefined,
+ schema_extra: dict[str, Any] | None = None,
) -> Any: ...
def Field(
default: Any = Undefined,
*,
- default_factory: Optional[NoArgAnyCallable] = None,
- alias: Optional[str] = None,
- validation_alias: Optional[str] = None,
- serialization_alias: Optional[str] = None,
- title: Optional[str] = None,
- field_title_generator: Optional[Callable[[str, PydanticFieldInfo], str]] = None,
- description: Optional[str] = None,
- examples: Optional[list[Any]] = None,
- deprecated: Union[Deprecated, str, bool, None] = None,
- exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- exclude_if: Optional[Callable[[Any], bool]] = None,
- include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
- const: Optional[bool] = None,
- gt: Optional[float] = None,
- ge: Optional[float] = None,
- lt: Optional[float] = None,
- le: Optional[float] = None,
- multiple_of: Optional[float] = None,
- max_digits: Optional[int] = None,
- decimal_places: Optional[int] = None,
- min_items: Optional[int] = None,
- max_items: Optional[int] = None,
- unique_items: Optional[bool] = None,
- min_length: Optional[int] = None,
- max_length: Optional[int] = None,
+ default_factory: NoArgAnyCallable | None = None,
+ alias: str | None = None,
+ validation_alias: str | None = None,
+ serialization_alias: str | None = None,
+ title: str | None = None,
++ field_title_generator: Callable[[str, PydanticFieldInfo], str] | None = None,
+ description: str | None = None,
++ examples: list[Any] | None = None,
++ deprecated: Deprecated | str | bool | None = None,
+ exclude: Set[int | str] | Mapping[int | str, Any] | Any = None,
++ exclude_if: Callable[[Any], bool] | None = None,
+ include: Set[int | str] | Mapping[int | str, Any] | Any = None,
+ const: bool | None = None,
+ gt: float | None = None,
+ ge: float | None = None,
+ lt: float | None = None,
+ le: float | None = None,
+ multiple_of: float | None = None,
+ max_digits: int | None = None,
+ decimal_places: int | None = None,
+ min_items: int | None = None,
+ max_items: int | None = None,
+ unique_items: bool | None = None,
+ min_length: int | None = None,
+ max_length: int | None = None,
allow_mutation: bool = True,
- regex: Optional[str] = None,
- strict: Optional[bool] = None,
- discriminator: Optional[str] = None,
+ regex: str | None = None,
++ strict: bool | None = None,
+ discriminator: str | None = None,
repr: bool = True,
- primary_key: Union[bool, UndefinedType] = Undefined,
+ primary_key: bool | UndefinedType = Undefined,
foreign_key: Any = Undefined,
- ondelete: Union[OnDeleteType, UndefinedType] = Undefined,
- unique: Union[bool, UndefinedType] = Undefined,
- nullable: Union[bool, UndefinedType] = Undefined,
- index: Union[bool, UndefinedType] = Undefined,
- sa_type: Union[type[Any], UndefinedType] = Undefined,
- sa_column: Union[Column, UndefinedType] = Undefined, # type: ignore
- sa_column_args: Union[Sequence[Any], UndefinedType] = Undefined,
- sa_column_kwargs: Union[Mapping[str, Any], UndefinedType] = Undefined,
- schema_extra: Optional[dict[str, Any]] = None,
+ ondelete: OnDeleteType | UndefinedType = Undefined,
+ unique: bool | UndefinedType = Undefined,
+ nullable: bool | UndefinedType = Undefined,
+ index: bool | UndefinedType = Undefined,
+ sa_type: type[Any] | UndefinedType = Undefined,
+ sa_column: Column | UndefinedType = Undefined, # type: ignore
+ sa_column_args: Sequence[Any] | UndefinedType = Undefined,
+ sa_column_kwargs: Mapping[str, Any] | UndefinedType = Undefined,
+ schema_extra: dict[str, Any] | None = None,
) -> Any:
current_schema_extra = schema_extra or {}
+
+ for param_name in (
+ "strict",
+ "examples",
+ "deprecated",
+ "exclude_if",
+ "field_title_generator",
+ ):
+ if param_name in current_schema_extra:
+ msg = f"Pass `{param_name}` parameter directly to Field instead of passing it via `schema_extra`"
+ warnings.warn(msg, DeprecationWarning, stacklevel=2)
+
# 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)
from decimal import Decimal
- from typing import Any, Literal, Optional, Union
-from typing import Literal
++from typing import Any, Literal
import pytest
from pydantic import ValidationError
instance = Model(id=123, foo="bar")
assert "foo=" not in repr(instance)
- id: Optional[int] = Field(default=None, primary_key=True)
+
+
+def test_strict_true():
+ class Model(SQLModel):
- id: Optional[int] = Field(default=None, primary_key=True)
++ id: int | None = Field(default=None, primary_key=True)
+ val: int
+ val_strict: int = Field(strict=True)
+
+ class ModelDB(Model, table=True):
+ pass
+
+ Model(val=123, val_strict=456)
+ Model(val="123", val_strict=456)
+
+ with pytest.raises(ValidationError):
+ Model(val=123, val_strict="456")
+
+ engine = create_engine("sqlite://", echo=True)
+
+ SQLModel.metadata.create_all(engine)
+
+ model = ModelDB(val=123, val_strict=456)
+ with Session(engine) as session:
+ session.add(model)
+ session.commit()
+ session.refresh(model)
+
+ assert model.val == 123
+ assert model.val_strict == 456
+
+
+def test_strict_table_model():
+ class Model(SQLModel, table=True):
- def test_strict_false(strict: Optional[bool]):
++ id: int | None = Field(default=None, primary_key=True)
+ val_strict: int = Field(strict=True)
+
+ engine = create_engine("sqlite://", echo=True)
+
+ SQLModel.metadata.create_all(engine)
+
+ model = Model(val_strict=456)
+ with Session(engine) as session:
+ session.add(model)
+ session.commit()
+ session.refresh(model)
+
+ assert model.val_strict == 456
+
+
+@pytest.mark.parametrize("strict", [None, False])
++def test_strict_false(strict: int | None):
+ class Model(SQLModel):
+ val: int = Field(strict=strict)
+
+ Model(val=123)
+ Model(val="123")
+
+
+def test_strict_via_schema_extra(): # Current workaround. Remove after some time
+ with pytest.warns(
+ DeprecationWarning,
+ match="Pass `strict` parameter directly to Field instead of passing it via `schema_extra`",
+ ):
+
+ class Model(SQLModel):
+ val: int
+ val_strict: int = Field(schema_extra={"strict": True})
+
+ Model(val=123, val_strict=456)
+ Model(val="123", val_strict=456)
+
+ with pytest.raises(ValidationError):
+ Model(val=123, val_strict="456")
+
+
+def test_examples():
+ class Model(SQLModel):
+ name: str = Field(examples=["Alice", "Bob"])
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["name"]["examples"] == ["Alice", "Bob"]
+
+
+def test_examples_via_schema_extra(): # Current workaround. Remove after some time
+ with pytest.warns(
+ DeprecationWarning,
+ match="Pass `examples` parameter directly to Field instead of passing it via `schema_extra`",
+ ):
+
+ class Model(SQLModel):
+ name: str = Field(schema_extra={"examples": ["Alice", "Bob"]})
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["name"]["examples"] == ["Alice", "Bob"]
+
+
+def test_deprecated():
+ class Model(SQLModel):
+ old_field: str = Field(deprecated=True)
+ another_old_field: str = Field(deprecated="This field is deprecated")
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["old_field"]["deprecated"] is True
+ assert model_schema["properties"]["another_old_field"]["deprecated"] is True
+
+
+def test_deprecated_via_schema_extra(): # Current workaround. Remove after some time
+ with pytest.warns(
+ DeprecationWarning,
+ match="Pass `deprecated` parameter directly to Field instead of passing it via `schema_extra`",
+ ):
+
+ class Model(SQLModel):
+ old_field: str = Field(schema_extra={"deprecated": True})
+ another_old_field: str = Field(
+ schema_extra={"deprecated": "This field is deprecated"}
+ )
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["old_field"]["deprecated"] is True
+ assert model_schema["properties"]["another_old_field"]["deprecated"] is True
+
+
+def test_exclude_if():
+ def is_empty_string(value: Any) -> bool:
+ return value == ""
+
+ class Model(SQLModel):
+ name: str = Field(exclude_if=is_empty_string)
+ age: int
+
+ model1 = Model(name="Alice", age=30)
+ model2 = Model(name="", age=25)
+
+ dict1 = model1.model_dump()
+ dict2 = model2.model_dump()
+
+ assert "name" in dict1
+ assert dict1["name"] == "Alice"
+
+ assert "name" not in dict2
+
+
+def test_exclude_if_via_schema_extra():
+ def is_empty_string(value: Any) -> bool:
+ return value == ""
+
+ with pytest.warns(
+ DeprecationWarning,
+ match="Pass `exclude_if` parameter directly to Field instead of passing it via `schema_extra`",
+ ):
+
+ class Model(SQLModel):
+ name: str = Field(schema_extra={"exclude_if": is_empty_string})
+ age: int
+
+ model1 = Model(name="Alice", age=30)
+ model2 = Model(name="", age=25)
+
+ dict1 = model1.model_dump()
+ dict2 = model2.model_dump()
+
+ assert "name" in dict1
+ assert dict1["name"] == "Alice"
+
+ assert "name" not in dict2
+
+
+def test_field_title_generator():
+ def upper(value: str, _: Any) -> str:
+ return value.upper()
+
+ class Model(SQLModel):
+ name: str = Field(field_title_generator=upper)
+ age: int
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["name"]["title"] == "NAME"
+ assert model_schema["properties"]["age"]["title"] == "Age"
+
+
+def test_field_title_generator_via_schema_extra():
+ def upper(value: str, _: Any) -> str:
+ return value.upper()
+
+ with pytest.warns(
+ DeprecationWarning,
+ match="Pass `field_title_generator` parameter directly to Field instead of passing it via `schema_extra`",
+ ):
+
+ class Model(SQLModel):
+ name: str = Field(schema_extra={"field_title_generator": upper})
+ age: int
+
+ model_schema = Model.model_json_schema()
+ assert model_schema["properties"]["name"]["title"] == "NAME"
+ assert model_schema["properties"]["age"]["title"] == "Age"