Compare commits
1 Commits
0.0.22
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fe31a31ac |
@@ -2,12 +2,6 @@
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.0.22
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix support for types with `Optional[Annoated[x, f()]]`, e.g. `id: Optional[pydantic.UUID4]`. PR [#1093](https://github.com/fastapi/sqlmodel/pull/1093) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#1085](https://github.com/fastapi/sqlmodel/pull/1085) by [@tiangolo](https://github.com/tiangolo).
|
||||
@@ -17,7 +11,6 @@
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Refactor test_enums to make them independent of previous imports. PR [#1095](https://github.com/fastapi/sqlmodel/pull/1095) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Update `latest-changes` GitHub Action. PR [#1087](https://github.com/fastapi/sqlmodel/pull/1087) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1028](https://github.com/fastapi/sqlmodel/pull/1028) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
* ⬆ Bump ruff from 0.4.7 to 0.6.2. PR [#1081](https://github.com/fastapi/sqlmodel/pull/1081) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
@@ -38,7 +31,7 @@
|
||||
* 💄 Add dark-mode logo. PR [#1061](https://github.com/tiangolo/sqlmodel/pull/1061) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔨 Update docs.py script to enable dirty reload conditionally. PR [#1060](https://github.com/tiangolo/sqlmodel/pull/1060) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update MkDocs previews. PR [#1058](https://github.com/tiangolo/sqlmodel/pull/1058) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 💄 Update Termynal line-height. PR [#1057](https://github.com/tiangolo/sqlmodel/pull/1057) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 💄 Update Termynal line-height. PR [#1057](https://github.com/tiangolo/sqlmodel/pull/1057) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Upgrade build docs configs. PR [#1047](https://github.com/tiangolo/sqlmodel/pull/1047) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Add alls-green for test-redistribute. PR [#1055](https://github.com/tiangolo/sqlmodel/pull/1055) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Update docs-previews to handle no docs changes. PR [#1056](https://github.com/tiangolo/sqlmodel/pull/1056) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
@@ -6,7 +6,7 @@ mypy ==1.4.1
|
||||
ruff ==0.6.2
|
||||
# For FastAPI tests
|
||||
fastapi >=0.103.2
|
||||
httpx ==0.24.1
|
||||
httpx ==0.27.2
|
||||
# TODO: upgrade when deprecating Python 3.7
|
||||
dirty-equals ==0.6.0
|
||||
jinja2 ==3.1.4
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = "0.0.22"
|
||||
__version__ = "0.0.21"
|
||||
|
||||
# Re-export from SQLAlchemy
|
||||
from sqlalchemy.engine import create_engine as create_engine
|
||||
|
||||
@@ -21,7 +21,7 @@ from typing import (
|
||||
from pydantic import VERSION as P_VERSION
|
||||
from pydantic import BaseModel
|
||||
from pydantic.fields import FieldInfo
|
||||
from typing_extensions import Annotated, get_args, get_origin
|
||||
from typing_extensions import get_args, get_origin
|
||||
|
||||
# Reassign variable to make it reexported for mypy
|
||||
PYDANTIC_VERSION = P_VERSION
|
||||
@@ -177,17 +177,16 @@ if IS_PYDANTIC_V2:
|
||||
return False
|
||||
return False
|
||||
|
||||
def get_sa_type_from_type_annotation(annotation: Any) -> Any:
|
||||
def get_type_from_field(field: Any) -> Any:
|
||||
type_: Any = field.annotation
|
||||
# Resolve Optional fields
|
||||
if annotation is None:
|
||||
if type_ is None:
|
||||
raise ValueError("Missing field type")
|
||||
origin = get_origin(annotation)
|
||||
origin = get_origin(type_)
|
||||
if origin is None:
|
||||
return annotation
|
||||
elif origin is Annotated:
|
||||
return get_sa_type_from_type_annotation(get_args(annotation)[0])
|
||||
return type_
|
||||
if _is_union_type(origin):
|
||||
bases = get_args(annotation)
|
||||
bases = get_args(type_)
|
||||
if len(bases) > 2:
|
||||
raise ValueError(
|
||||
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||
@@ -198,14 +197,9 @@ if IS_PYDANTIC_V2:
|
||||
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||
)
|
||||
# Optional unions are allowed
|
||||
use_type = bases[0] if bases[0] is not NoneType else bases[1]
|
||||
return get_sa_type_from_type_annotation(use_type)
|
||||
return bases[0] if bases[0] is not NoneType else bases[1]
|
||||
return origin
|
||||
|
||||
def get_sa_type_from_field(field: Any) -> Any:
|
||||
type_: Any = field.annotation
|
||||
return get_sa_type_from_type_annotation(type_)
|
||||
|
||||
def get_field_metadata(field: Any) -> Any:
|
||||
for meta in field.metadata:
|
||||
if isinstance(meta, (PydanticMetadata, MaxLen)):
|
||||
@@ -450,7 +444,7 @@ else:
|
||||
)
|
||||
return field.allow_none # type: ignore[no-any-return, attr-defined]
|
||||
|
||||
def get_sa_type_from_field(field: Any) -> Any:
|
||||
def get_type_from_field(field: Any) -> Any:
|
||||
if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
|
||||
return field.type_
|
||||
raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
|
||||
|
||||
@@ -71,7 +71,7 @@ from ._compat import ( # type: ignore[attr-defined]
|
||||
get_field_metadata,
|
||||
get_model_fields,
|
||||
get_relationship_to,
|
||||
get_sa_type_from_field,
|
||||
get_type_from_field,
|
||||
init_pydantic_private_attrs,
|
||||
is_field_noneable,
|
||||
is_table_model_class,
|
||||
@@ -649,7 +649,7 @@ def get_sqlalchemy_type(field: Any) -> Any:
|
||||
if sa_type is not Undefined:
|
||||
return sa_type
|
||||
|
||||
type_ = get_sa_type_from_field(field)
|
||||
type_ = get_type_from_field(field)
|
||||
metadata = get_field_metadata(field)
|
||||
|
||||
# Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||
|
||||
from tests.conftest import needs_pydanticv2
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_annotated_optional_types(clear_sqlmodel) -> None:
|
||||
from pydantic import UUID4
|
||||
|
||||
class Hero(SQLModel, table=True):
|
||||
# Pydantic UUID4 is: Annotated[UUID, UuidVersion(4)]
|
||||
id: Optional[UUID4] = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
|
||||
engine = create_engine("sqlite:///:memory:")
|
||||
SQLModel.metadata.create_all(engine)
|
||||
with Session(engine) as db:
|
||||
hero = Hero()
|
||||
db.add(hero)
|
||||
db.commit()
|
||||
statement = select(Hero)
|
||||
result = db.exec(statement).all()
|
||||
assert len(result) == 1
|
||||
assert isinstance(hero.id, uuid.UUID)
|
||||
@@ -1,11 +1,10 @@
|
||||
import importlib
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import create_mock_engine
|
||||
from sqlalchemy.sql.type_api import TypeEngine
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from . import test_enums_models
|
||||
from .conftest import needs_pydanticv1, needs_pydanticv2
|
||||
|
||||
"""
|
||||
@@ -17,6 +16,30 @@ Associated issues:
|
||||
"""
|
||||
|
||||
|
||||
class MyEnum1(str, enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
|
||||
|
||||
class MyEnum2(str, enum.Enum):
|
||||
C = "C"
|
||||
D = "D"
|
||||
|
||||
|
||||
class BaseModel(SQLModel):
|
||||
id: uuid.UUID = Field(primary_key=True)
|
||||
enum_field: MyEnum2
|
||||
|
||||
|
||||
class FlatModel(SQLModel, table=True):
|
||||
id: uuid.UUID = Field(primary_key=True)
|
||||
enum_field: MyEnum1
|
||||
|
||||
|
||||
class InheritModel(BaseModel, table=True):
|
||||
pass
|
||||
|
||||
|
||||
def pg_dump(sql: TypeEngine, *args, **kwargs):
|
||||
dialect = sql.compile(dialect=postgres_engine.dialect)
|
||||
sql_str = str(dialect).rstrip()
|
||||
@@ -35,9 +58,7 @@ postgres_engine = create_mock_engine("postgresql://", pg_dump)
|
||||
sqlite_engine = create_mock_engine("sqlite://", sqlite_dump)
|
||||
|
||||
|
||||
def test_postgres_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
||||
assert test_enums_models, "Ensure the models are imported and registered"
|
||||
importlib.reload(test_enums_models)
|
||||
def test_postgres_ddl_sql(capsys):
|
||||
SQLModel.metadata.create_all(bind=postgres_engine, checkfirst=False)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
@@ -45,19 +66,17 @@ def test_postgres_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
||||
assert "CREATE TYPE myenum2 AS ENUM ('C', 'D');" in captured.out
|
||||
|
||||
|
||||
def test_sqlite_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
||||
assert test_enums_models, "Ensure the models are imported and registered"
|
||||
importlib.reload(test_enums_models)
|
||||
def test_sqlite_ddl_sql(capsys):
|
||||
SQLModel.metadata.create_all(bind=sqlite_engine, checkfirst=False)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "enum_field VARCHAR(1) NOT NULL" in captured.out, captured
|
||||
assert "enum_field VARCHAR(1) NOT NULL" in captured.out
|
||||
assert "CREATE TYPE" not in captured.out
|
||||
|
||||
|
||||
@needs_pydanticv1
|
||||
def test_json_schema_flat_model_pydantic_v1():
|
||||
assert test_enums_models.FlatModel.schema() == {
|
||||
assert FlatModel.schema() == {
|
||||
"title": "FlatModel",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -78,7 +97,7 @@ def test_json_schema_flat_model_pydantic_v1():
|
||||
|
||||
@needs_pydanticv1
|
||||
def test_json_schema_inherit_model_pydantic_v1():
|
||||
assert test_enums_models.InheritModel.schema() == {
|
||||
assert InheritModel.schema() == {
|
||||
"title": "InheritModel",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -99,7 +118,7 @@ def test_json_schema_inherit_model_pydantic_v1():
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_json_schema_flat_model_pydantic_v2():
|
||||
assert test_enums_models.FlatModel.model_json_schema() == {
|
||||
assert FlatModel.model_json_schema() == {
|
||||
"title": "FlatModel",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -115,7 +134,7 @@ def test_json_schema_flat_model_pydantic_v2():
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_json_schema_inherit_model_pydantic_v2():
|
||||
assert test_enums_models.InheritModel.model_json_schema() == {
|
||||
assert InheritModel.model_json_schema() == {
|
||||
"title": "InheritModel",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class MyEnum1(str, enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
|
||||
|
||||
class MyEnum2(str, enum.Enum):
|
||||
C = "C"
|
||||
D = "D"
|
||||
|
||||
|
||||
class BaseModel(SQLModel):
|
||||
id: uuid.UUID = Field(primary_key=True)
|
||||
enum_field: MyEnum2
|
||||
|
||||
|
||||
class FlatModel(SQLModel, table=True):
|
||||
id: uuid.UUID = Field(primary_key=True)
|
||||
enum_field: MyEnum1
|
||||
|
||||
|
||||
class InheritModel(BaseModel, table=True):
|
||||
pass
|
||||
Reference in New Issue
Block a user