| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -242,6 +242,9 @@ def sqlmodel_table_construct( | |||
| 242 | 242 | _extra = {} | |
| 243 | 243 | for k, v in values.items(): | |
| 244 | 244 | _extra[k] = v | |
| 245 | + setattr_ = ( | ||
| 246 | + object.__setattr__ if self_instance.model_config.get("frozen") else setattr | ||
| 247 | + ) | ||
| 245 | 248 | # SQLModel override, do not include everything, only the model fields | |
| 246 | 249 | # else: | |
| 247 | 250 | # fields_values.update(values) | |
@@ -251,7 +254,7 @@ def sqlmodel_table_construct( | |||
| 251 | 254 | # object.__setattr__(new_obj, "__dict__", fields_values) | |
| 252 | 255 | # instrumentation | |
| 253 | 256 | for key, value in {**old_dict, **fields_values}.items(): | |
| 254 | - setattr(self_instance, key, value) | ||
| 257 | + setattr_(self_instance, key, value) | ||
| 255 | 258 | # End SQLModel override | |
| 256 | 259 | object.__setattr__(self_instance, "__pydantic_fields_set__", _fields_set) | |
| 257 | 260 | if not cls.__pydantic_root_model__: | |
@@ -268,7 +271,7 @@ def sqlmodel_table_construct( | |||
| 268 | 271 | for key in self_instance.__sqlmodel_relationships__: | |
| 269 | 272 | value = values.get(key, Undefined) | |
| 270 | 273 | if value is not Undefined: | |
| 271 | - setattr(self_instance, key, value) | ||
| 274 | + setattr_(self_instance, key, value) | ||
| 272 | 275 | # End SQLModel override | |
| 273 | 276 | return self_instance | |
| 274 | 277 | ||
@@ -305,6 +308,7 @@ def sqlmodel_validate( | |||
| 305 | 308 | context=context, | |
| 306 | 309 | self_instance=new_obj, | |
| 307 | 310 | ) | |
| 311 | + setattr_ = object.__setattr__ if new_obj.model_config.get("frozen") else setattr | ||
| 308 | 312 | # Capture fields set to restore it later | |
| 309 | 313 | fields_set = new_obj.__pydantic_fields_set__.copy() | |
| 310 | 314 | if not is_table_model_class(cls): | |
@@ -314,15 +318,15 @@ def sqlmodel_validate( | |||
| 314 | 318 | # Do not set __dict__, instead use setattr to trigger SQLAlchemy | |
| 315 | 319 | # instrumentation | |
| 316 | 320 | for key, value in {**old_dict, **new_obj.__dict__}.items(): | |
| 317 | - setattr(new_obj, key, value) | ||
| 321 | + setattr_(new_obj, key, value) | ||
| 318 | 322 | # Restore fields set | |
| 319 | 323 | object.__setattr__(new_obj, "__pydantic_fields_set__", fields_set) | |
| 320 | 324 | # Get and set any relationship objects | |
| 321 | 325 | if is_table_model_class(cls): | |
| 322 | 326 | for key in new_obj.__sqlmodel_relationships__: | |
| 323 | 327 | value = getattr(use_obj, key, Undefined) | |
| 324 | 328 | if value is not Undefined: | |
| 325 | - setattr(new_obj, key, value) | ||
| 329 | + setattr_(new_obj, key, value) | ||
| 326 | 330 | return new_obj | |
| 327 | 331 | ||
| 328 | 332 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,80 @@ | |||
| 1 | + import pytest | ||
| 2 | + from pydantic import ConfigDict, ValidationError | ||
| 3 | + from sqlmodel import Field, Session, SQLModel, create_engine, select | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + def test_frozen_non_table_model_creation(clear_sqlmodel): | ||
| 7 | + class HeroBase(SQLModel): | ||
| 8 | + model_config = ConfigDict(frozen=True) | ||
| 9 | + | ||
| 10 | + name: str | ||
| 11 | + age: int | None = None | ||
| 12 | + | ||
| 13 | + hero = HeroBase(name="Deadpond", age=30) | ||
| 14 | + | ||
| 15 | + assert hero.name == "Deadpond" | ||
| 16 | + assert hero.age == 30 | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + def test_frozen_non_table_model_is_immutable(clear_sqlmodel): | ||
| 20 | + class HeroBase(SQLModel): | ||
| 21 | + model_config = ConfigDict(frozen=True) | ||
| 22 | + | ||
| 23 | + name: str | ||
| 24 | + age: int | None = None | ||
| 25 | + | ||
| 26 | + hero = HeroBase(name="Deadpond", age=30) | ||
| 27 | + | ||
| 28 | + with pytest.raises((ValidationError, TypeError)): | ||
| 29 | + hero.name = "Spider-Boy" # type: ignore[misc] | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + def test_frozen_table_model_creation(clear_sqlmodel): | ||
| 33 | + class Hero(SQLModel, table=True): | ||
| 34 | + model_config = ConfigDict(frozen=True) | ||
| 35 | + | ||
| 36 | + id: int | None = Field(default=None, primary_key=True) | ||
| 37 | + name: str | ||
| 38 | + age: int | None = None | ||
| 39 | + | ||
| 40 | + hero = Hero(name="Deadpond", age=30) | ||
| 41 | + | ||
| 42 | + assert hero.name == "Deadpond" | ||
| 43 | + assert hero.age == 30 | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + def test_frozen_table_model_persists_and_retrieves(clear_sqlmodel): | ||
| 47 | + class Hero(SQLModel, table=True): | ||
| 48 | + model_config = ConfigDict(frozen=True) | ||
| 49 | + | ||
| 50 | + id: int | None = Field(default=None, primary_key=True) | ||
| 51 | + name: str | ||
| 52 | + age: int | None = None | ||
| 53 | + | ||
| 54 | + engine = create_engine("sqlite://") | ||
| 55 | + SQLModel.metadata.create_all(engine) | ||
| 56 | + | ||
| 57 | + with Session(engine) as session: | ||
| 58 | + hero = Hero(name="Deadpond", age=30) | ||
| 59 | + session.add(hero) | ||
| 60 | + session.commit() | ||
| 61 | + session.refresh(hero) | ||
| 62 | + | ||
| 63 | + with Session(engine) as session: | ||
| 64 | + retrieved = session.exec(select(Hero)).one() | ||
| 65 | + assert retrieved.name == "Deadpond" | ||
| 66 | + assert retrieved.age == 30 | ||
| 67 | + | ||
| 68 | + | ||
| 69 | + def test_frozen_table_model_validate(clear_sqlmodel): | ||
| 70 | + class Hero(SQLModel, table=True): | ||
| 71 | + model_config = ConfigDict(frozen=True) | ||
| 72 | + | ||
| 73 | + id: int | None = Field(default=None, primary_key=True) | ||
| 74 | + name: str | ||
| 75 | + age: int | None = None | ||
| 76 | + | ||
| 77 | + hero = Hero.model_validate({"name": "Deadpond", "age": 30}) | ||
| 78 | + | ||
| 79 | + assert hero.name == "Deadpond" | ||
| 80 | + assert hero.age == 30 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments