-
-
Notifications
You must be signed in to change notification settings - Fork 781
Support hybrid_property, column_property, declared_attr #801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
50Bytes-dev
wants to merge
64
commits into
fastapi:main
Choose a base branch
from
50Bytes-dev:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
64 commits
Select commit
Hold shift + click to select a range
5471ece
Support hybrid_property
50Bytes-dev c363ce6
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 5bf07f8
fix
50Bytes-dev 224c74b
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 3f82be3
fix
50Bytes-dev 953c01d
Merge remote-tracking branch 'origin/main'
50Bytes-dev e5dad94
add declared_attr, column_property support
50Bytes-dev 6bfff90
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] e5bb32a
Merge branch 'tiangolo:main' into main
50Bytes-dev 5ade49a
fix tests
50Bytes-dev b6e2caf
fix tests
50Bytes-dev e0b201d
Update sqlmodel/main.py
50Bytes-dev 7ac7889
Update sqlmodel/main.py
50Bytes-dev bb25d90
Update sqlmodel/main.py
50Bytes-dev 8da2993
Merge branch 'tiangolo:main' into main
50Bytes-dev 7a53368
fix
50Bytes-dev 87d9c02
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 0fa2d40
fix
50Bytes-dev 7f0aa44
Merge branch 'main' of github.com:50Bytes-dev/sqlmodel
50Bytes-dev 2e76370
fix
50Bytes-dev a6b81de
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 23929c3
Merge branch 'tiangolo:main' into main
50Bytes-dev d5aa7dd
Merge branch 'tiangolo:main' into main
50Bytes-dev f4dadc5
fix
50Bytes-dev fba4308
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 4afc41b
fix list type
50Bytes-dev 7799fe9
Merge branch 'main' of github.com:50Bytes-dev/sqlmodel
50Bytes-dev d06c75a
add validation_alias
50Bytes-dev c980f31
fix
50Bytes-dev 74d3129
Merge remote-tracking branch 'upstream/main'
50Bytes-dev 1fae234
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 88acd2a
fix
50Bytes-dev 47b88bf
Merge branch 'main' of github.com:50Bytes-dev/sqlmodel
50Bytes-dev b08c757
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] 9db90a5
fix
50Bytes-dev ecd3997
Merge branch 'main' of github.com:50Bytes-dev/sqlmodel
50Bytes-dev 14c5aba
add association proxy support
50Bytes-dev 44e0cbe
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] b1ae757
fix: update return type of col function to Column
50Bytes-dev 0c96495
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] fdcd531
fix: extend sa_column type to include MappedSQLExpression
50Bytes-dev cf6b48b
Merge branch 'main' of github.com:50Bytes-dev/sqlmodel
50Bytes-dev b991725
fix
50Bytes-dev 683b0c7
fix
50Bytes-dev e141966
Add comprehensive tests for Pydantic to SQLModel conversion and relat…
50Bytes-dev 1229a44
refactor: streamline relationship update tests for Pydantic to SQLMod…
50Bytes-dev 244069f
Fix: Correct relationship updates with forward references and test logic
google-labs-jules[bot] b5d7b2d
Merge pull request #1 from 50Bytes-dev/fix/relationship-update-forwar…
50Bytes-dev c875330
feat: enhance relationship handling for Pydantic to SQLModel conversi…
50Bytes-dev 170b343
feat: enhance handling of association proxies in SQLModel initialization
50Bytes-dev c6f39f0
chore: add pdm.lock and .pdm-python to .gitignore
50Bytes-dev ddd0474
feat: add support for hybrid properties with setters in SQLModel and …
50Bytes-dev 0566470
feat: implement __sqlalchemy_hybrid_property_setters__ for hybrid pro…
50Bytes-dev e4a89e8
feat: add deferred_column_property for safe deferred loading in SQLModel
50Bytes-dev 5d255c5
feat: enhance async handling in SafeDeferredColumnLoader and add test…
50Bytes-dev ad7e957
feat: implement SafeAttributeWrapper for improved async handling and …
50Bytes-dev 4ea632a
feat: implement event-based fallback for deferred_column_property; ad…
50Bytes-dev 08e57d9
feat: refactor deferred_column_property to use standard ColumnPropert…
50Bytes-dev 8e9fe31
feat: enhance deferred_column_property with additional parameters and…
50Bytes-dev 5ff1e71
feat: update col function to return QueryableAttribute for improved t…
50Bytes-dev e9c74ee
feat: refactor compatibility handling for Pydantic v2; introduce new …
50Bytes-dev a6db8af
feat: add new utility functions for SQLModel to handle relations and …
50Bytes-dev fef15c5
feat: import new utility functions for handling columns by schema and…
50Bytes-dev 833b870
feat: update validation_alias type in Field function to support Alias…
50Bytes-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| from typing import List, Optional | ||
|
|
||
| from sqlalchemy import case, create_engine, func | ||
| from sqlalchemy.orm import column_property, declared_attr | ||
| from sqlmodel import Field, Relationship, Session, SQLModel, select | ||
|
|
||
|
|
||
| def test_query(clear_sqlmodel): | ||
| class Item(SQLModel, table=True): | ||
| id: Optional[int] = Field(default=None, primary_key=True) | ||
| value: float | ||
| hero_id: int = Field(foreign_key="hero.id") | ||
| hero: "Hero" = Relationship(back_populates="items") | ||
|
|
||
| class Hero(SQLModel, table=True): | ||
| id: Optional[int] = Field(default=None, primary_key=True) | ||
| name: str | ||
| items: List[Item] = Relationship(back_populates="hero") | ||
|
|
||
| @declared_attr | ||
| def total_items(cls): | ||
| return column_property(cls._total_items_expression()) | ||
|
|
||
| @classmethod | ||
| def _total_items_expression(cls): | ||
| return ( | ||
| select(func.coalesce(func.sum(Item.value), 0)) | ||
| .where(Item.hero_id == cls.id) | ||
| .correlate_except(Item) | ||
| .label("total_items") | ||
| ) | ||
|
|
||
| @declared_attr | ||
| def status(cls): | ||
| return column_property( | ||
| select( | ||
| case( | ||
| (cls._total_items_expression() > 0, "active"), else_="inactive" | ||
| ) | ||
| ).scalar_subquery() | ||
| ) | ||
|
|
||
| hero_1 = Hero(name="Deadpond") | ||
| hero_2 = Hero(name="Spiderman") | ||
|
|
||
| engine = create_engine("sqlite://") | ||
|
|
||
| SQLModel.metadata.create_all(engine) | ||
| with Session(engine) as session: | ||
| session.add(hero_1) | ||
| session.add(hero_2) | ||
| session.commit() | ||
| session.refresh(hero_1) | ||
| session.refresh(hero_2) | ||
|
|
||
| item_1 = Item(value=1.0, hero_id=hero_1.id) | ||
| item_2 = Item(value=2.0, hero_id=hero_1.id) | ||
|
|
||
| with Session(engine) as session: | ||
| session.add(item_1) | ||
| session.add(item_2) | ||
| session.commit() | ||
| session.refresh(item_1) | ||
| session.refresh(item_2) | ||
|
|
||
| with Session(engine) as session: | ||
| hero_statement = select(Hero).where(Hero.total_items > 0.0) | ||
| hero = session.exec(hero_statement).first() | ||
| assert hero.name == "Deadpond" | ||
| assert hero.total_items == 3.0 | ||
| assert hero.status == "active" | ||
|
|
||
| with Session(engine) as session: | ||
| hero_statement = select(Hero).where( | ||
| Hero.status == "inactive", | ||
| ) | ||
| hero = session.exec(hero_statement).first() | ||
| assert hero.name == "Spiderman" | ||
| assert hero.total_items == 0.0 | ||
| assert hero.status == "inactive" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| from typing import List, Optional | ||
|
|
||
| from sqlalchemy import case, create_engine, func | ||
| from sqlalchemy.ext.hybrid import hybrid_property | ||
| from sqlmodel import Field, Relationship, Session, SQLModel, select | ||
|
|
||
|
|
||
| def test_query(clear_sqlmodel): | ||
| class Item(SQLModel, table=True): | ||
| id: Optional[int] = Field(default=None, primary_key=True) | ||
| value: float | ||
| hero_id: int = Field(foreign_key="hero.id") | ||
| hero: "Hero" = Relationship(back_populates="items") | ||
|
|
||
| class Hero(SQLModel, table=True): | ||
| id: Optional[int] = Field(default=None, primary_key=True) | ||
| name: str | ||
| items: List[Item] = Relationship(back_populates="hero") | ||
|
|
||
| @hybrid_property | ||
| def total_items(self): | ||
| return sum([item.value for item in self.items], 0) | ||
|
|
||
| @total_items.inplace.expression | ||
| @classmethod | ||
| def _total_items_expression(cls): | ||
| return ( | ||
| select(func.coalesce(func.sum(Item.value), 0)) | ||
| .where(Item.hero_id == cls.id) | ||
| .correlate(cls) | ||
| .label("total_items") | ||
| ) | ||
|
|
||
| @hybrid_property | ||
| def status(self): | ||
| return "active" if self.total_items > 0 else "inactive" | ||
|
|
||
| @status.inplace.expression | ||
| @classmethod | ||
| def _status_expression(cls): | ||
| return select( | ||
| case((cls.total_items > 0, "active"), else_="inactive") | ||
| ).label("status") | ||
|
|
||
| hero_1 = Hero(name="Deadpond") | ||
| hero_2 = Hero(name="Spiderman") | ||
|
|
||
| engine = create_engine("sqlite://") | ||
|
|
||
| SQLModel.metadata.create_all(engine) | ||
| with Session(engine) as session: | ||
| session.add(hero_1) | ||
| session.add(hero_2) | ||
| session.commit() | ||
| session.refresh(hero_1) | ||
| session.refresh(hero_2) | ||
|
|
||
| item_1 = Item(value=1.0, hero_id=hero_1.id) | ||
| item_2 = Item(value=2.0, hero_id=hero_1.id) | ||
|
|
||
| with Session(engine) as session: | ||
| session.add(item_1) | ||
| session.add(item_2) | ||
| session.commit() | ||
| session.refresh(item_1) | ||
| session.refresh(item_2) | ||
|
|
||
| with Session(engine) as session: | ||
| hero_statement = select(Hero).where(Hero.total_items > 0.0) | ||
| hero = session.exec(hero_statement).first() | ||
| assert hero.total_items == 3.0 | ||
| assert hero.status == "active" | ||
|
|
||
| with Session(engine) as session: | ||
| hero_statement = select(Hero).where( | ||
| Hero.status == "inactive", | ||
| ) | ||
| hero = session.exec(hero_statement).first() | ||
| assert hero.total_items == 0.0 | ||
| assert hero.status == "inactive" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.