Skip to content

Conversation

@50Bytes-dev
Copy link

    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")
            )

@50Bytes-dev 50Bytes-dev changed the title Support hybrid_property Support hybrid_property, column_property, declared_attr Mar 1, 2024
@50Bytes-dev
Copy link
Author

    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()
            )

Copy link

@awoimbee awoimbee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just small suggestions from a random outsider :p (some suggestions might be wrong).
All in all, this is clean and adds a lot of functionality, thanks !
You should look into this @tiangolo !

50Bytes-dev and others added 3 commits March 7, 2024 17:18
Co-authored-by: Arthur Woimbée <[email protected]>
Co-authored-by: Arthur Woimbée <[email protected]>
Co-authored-by: Arthur Woimbée <[email protected]>
@muazhari
Copy link

Please merge it asap :3. I need it, lol.

@mfkaroui
Copy link

hey can we get this merged in

@mfkaroui
Copy link

mfkaroui commented Apr 4, 2024

@tiangolo Could you please take a look at this MR. The functionality is very nice. Would be a nice addition. Doesn't seem to clash with overall design

@francomahl
Copy link

    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()
            )

Following this example I am getting

pydantic.errors.PydanticUserError: A non-annotated attribute was detected: `total_items = <sqlalchemy.orm.decl_api.declared_attr object at 0x10e415350>`. All model fields require a type annotation; if `total_items` is not meant to be a field, you may be able to resolve this error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.

Even if I do def total_items(cls): -> int I still get the same error.
SQLModel version 0.0.16

But I think we can get the same result with computed_field and the property decorator

    from pydantic import computed_field

    class Hero(SQLModel, table=True):
        id: Optional[int] = Field(default=None, primary_key=True)
        name: str
        items: List[Item] = Relationship(back_populates="hero")

        @computed_field
        @property
        def total_items(cls) -> int:
            return len(self.items)

@raphaellaude
Copy link

Any movement to get this functionality in?

@tstorek
Copy link

tstorek commented Jun 26, 2025

Hi,
I am awaiting this feature as it would simplify my life a lot.
I there any progress on this? Otherwise I need to go back to sql_alchemy

Cheers

@50Bytes-dev
Copy link
Author

Hi, I am awaiting this feature as it would simplify my life a lot. I there any progress on this? Otherwise I need to go back to sql_alchemy

Cheers

Hi. Just use my repo https://github.com/50Bytes-dev/sqlmodel/tree/main

@tstorek
Copy link

tstorek commented Jun 26, 2025

Hi, I am awaiting this feature as it would simplify my life a lot. I there any progress on this? Otherwise I need to go back to sql_alchemy
Cheers

Hi. Just use my repo https://github.com/50Bytes-dev/sqlmodel/tree/main

Thanks, no offence but I would prefer to stick with the official repro to keep everythin in line. Isn't there a chance for a merge any time soon?

@50Bytes-dev
Copy link
Author

Hi, I am awaiting this feature as it would simplify my life a lot. I there any progress on this? Otherwise I need to go back to sql_alchemy
Cheers

Hi. Just use my repo https://github.com/50Bytes-dev/sqlmodel/tree/main

Thanks, no offence but I would prefer to stick with the official repro to keep everythin in line. Isn't there a chance for a merge any time soon?

This repository has long been abandoned, which is why I made a fork and am adding needed functionality there

@sakost
Copy link

sakost commented Aug 25, 2025

any updates on this?

@github-actions github-actions bot added the conflicts Automatically generated when a PR has a merge conflict label Sep 5, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Sep 5, 2025

This pull request has a merge conflict that needs to be resolved.

@Vizonex
Copy link

Vizonex commented Sep 25, 2025

I think I can go a bit further than what this pr had in mind. I was thinking about combining computed_feilds and hybrid_property column_property and declared_attr together. Does anyone have any tips and suggestions? I'll try and use what this pr had in mind as a blueprint.

…configuration and utility functions for SQLModel
@Vizonex
Copy link

Vizonex commented Oct 22, 2025

@50Bytes-dev Glad to see your pr is still alive, mind if I help you review it?

…columns by schema; update col function to return InstrumentedAttribute for better type safety
…Path and AliasChoices for enhanced flexibility
@Vizonex
Copy link

Vizonex commented Nov 21, 2025

@50Bytes-dev Have you tried adding some tests to the pytest to ensure that this implementation works?

@50Bytes-dev
Copy link
Author

@50Bytes-dev Have you tried adding some tests to the pytest to ensure that this implementation works?

Yes, I have basic tests. I use this PR in the production of my projects and make sure that everything works correctly.

@tstorek
Copy link

tstorek commented Nov 26, 2025

@50Bytes-dev thanks, for that piece of code. Is there any chance for release some time soon. I have a use case starting from next week and I'd like to omit a vanilla sqlalchemy workaround :)

@50Bytes-dev
Copy link
Author

@50Bytes-dev thanks, for that piece of code. Is there any chance for release some time soon. I have a use case starting from next week and I'd like to omit a vanilla sqlalchemy workaround :)

Release where? Personally, I just use the repository and don't plan to release anything anywhere. I recommend trying it this way too, it's quite convenient.

dependencies = [
  "sqlmodel",
]

[tool.uv.sources]
sqlmodel = { git = "https://github.com/50Bytes-dev/sqlmodel.git", rev = "833b8700e5006177d1a57fd8b3109bd625a358de" }

@tstorek
Copy link

tstorek commented Nov 26, 2025

@50Bytes-dev Okay, sorry. I thought that the PR is meant to be merged at some point.

@50Bytes-dev
Copy link
Author

@50Bytes-dev Okay, sorry. I thought that the PR is meant to be merged at some point.

I think that the main repository is dead and unlikely to be developed further, so I created my own fork where I implement all the necessary functions myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicts Automatically generated when a PR has a merge conflict feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.