-
-
Notifications
You must be signed in to change notification settings - Fork 666
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
How to create computed columns ? #150
Comments
I tried using E fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that <class 'sqlalchemy.ext.hybrid.hybrid_property'> is a valid pydantic field type And if I set : from pydantic import BaseConfig
BaseConfig.arbitrary_types_allowed = True We now have : ValueError: [TypeError("'wrapper_descriptor' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] |
Hi @sorasful, So basically you want the To do so I would do the following: from sqlmodel import SQLModel
from pydantic import root_validator
from typing import Optional
class Album(SQLModel):
title: str
slug: Optional[str]
description: str
@root_validator
def create_slug(cls, values):
title = values.get("title")
slugify_title = slugify(title)
values["slug"] = slugify_title
return values The input: {
"title": "My Album Title",
"description": "This is a description"
} The output I got: {
"title": "My Album Title",
"description": "This is a description",
"slug": "my-album-title"
} |
@FabiEbert Hello, yeah that's what I ended up doing. But I don't feel like is the cleaner way you know. Also, if you want the slug to be updated each time you update the title you need to add this in your model class Config:
validate_assignment = True |
Any other thoughts on how to actually use the computed_property() from SQL alchemy? E.g.
This results in in "column Parent.child_count does not exist." |
Related to this issue #240 |
I think this is one of biggest SQLModel drawbacks I've encountered. I am trying to use the root validator in a 'mix-in' class, in order to calculate an hash-value on a subset of the model fields. class ModelWithHashField(BaseModel):
final_hash: str = ""
@root_validator(pre=False,
skip_on_failure=True)
def calculate_global_hash(cls, values) -> Dict:
values["final_hash"] = sha256_hash_values(
values['required_field1'], values['required_field2']
)
return values
class MyModel(SQLModel, ModelWithHashField, table=True):
required_field1: str
required_field2: str |
Pydantic v2.0 adds support for computed fields, which should allow computed columns once Pydantic V2 support is added (related to #532, #621) |
This worked for me @computed_field(return_type=str)
@declared_attr
def hello_world(self):
return column_property(
func.concat('hello ', "world")
) but could not do a query Update, this is working: # from the Conversation table
@computed_field(return_type=Optional[bool])
@property
def has_any_unread_message(self):
if session := object_session(self):
return (
session.exec(
select(Message)
.where(Message.conversation_id == self.id)
.limit(1)
).first()
is not None
) |
Workaround: Generating a dynamic property (this does NOT add a column to the database)As @samidarko already mentioned, with pydantic 2.0's from sqlmodel import SQLModel
from pydantic import computed_field
from slugify import slugify
class Album(SQLModel):
title: str
description: str
@computed_field
@property
def slug(self) -> str:
return slugify(self.title) Result: >>> Album(title='A Title With Many Spaces', description='I am the description')
Album(title='A Title With Many Spaces', description='I am the description', slug='a-title-with-many-spaces') Note that this solution computes the 'column' each time it is accessed and does not create a new column in the database. However, when disregarding efficiency considerations, I believe that this is a good workaround for many cases. Application to other cases class Article(SQLModel, table=True):
__tablename__ = "food_article"
id: int | None = Field(default=None, primary_key=True)
price_history: list["PriceHistory"] = Relationship()
@computed_field
@property
def unit_price_gross(self) -> float | None:
"""Returns the most recent price from self.price_history."""
if self.price_history:
prices_sorted = sorted(
self.price_history,
key=lambda item: item.valid_from,
reverse=True
)
current_price = prices_sorted[0].unit_price_gross
return current_price
else:
return None |
@ErikFub in your example I believe OPs original question wanted the slug field/value to be persisted to the database. I'm using the latest sqlmodel version 0.14 and pydantic 2.0. |
@gavinest True that, I missed the part that it should persist in the database - thanks for pointing that out! I've adjusted my answer accordingly |
Use my PR #801 |
First Check
Commit to Help
Example Code
Description
I'm trying to generate a column named "slug" that is a slugified version of "title" so it should be persisted in database, and be updated when title changes.
But so far no luck, I've looked at column property but didn't manage to make it work with SQLModel. I saw that there are events "before update" ... but I don't think this is the way to go
Operating System
Windows
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
3.9.2
Additional Context
No response
The text was updated successfully, but these errors were encountered: