sqlmodel
sqlmodel copied to clipboard
There seems to be a bug in using model with aliased fields to define request body
First Check
- [x] I added a very descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the SQLModel documentation, with the integrated search.
- [X] I already searched in Google "How to X in SQLModel" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to SQLModel but to Pydantic.
- [X] I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
- [x] I commit to help with one of those options 👆
Example Code
from typing import Optional
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str = Field(alias='secretName')
age: Optional[int] = Field(default=None, index=True)
class HeroPydantic(BaseModel):
id: Optional[int] = Field(default=None)
name: str = Field()
secret_name: str = Field(alias='secretName')
age: Optional[int] = Field(default=None)
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
app = FastAPI()
@app.post("/heroes/")
def create_hero(hero: Hero):
with Session(engine) as session:
session.add(hero)
session.commit()
session.refresh(hero)
return hero
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8081)
Description
- Create
Hero
model, thesecret_name
field aliased tosecretName
- Post data
{
"name": "string",
"secretName": "string",
"age": 0
}
- Then get
hero.secret_name
isNone
- If I replace
hero: Hero
tohero: HeroPydantic
, thehero.secret_name
can get correct value from post data. But the strainge thing is that I can get correcthero.secret_name
byhero: Hero = Hero.parse_obj(hero.dict(by_alias=True))
So it seems that alias argument in sqlmodel seems not working for converting body from post data, I have to write two models in order to achieve that.
Operating System
Windows
Operating System Details
No response
SQLModel Version
0.0.6
Python Version
3.7.6
Additional Context
No response
I found that:
- Fastapi generate
hero
object from request body by functionfastapi.dependencies.utils.request_body_to_args
, and actually generatehero
by this linev_, errors_ = field.validate(value, values, loc=loc)
- Then call
sqlmodel.SQLModel.validate
. In this function, transform alias dictvalue
to field name dictvalues
byvalues, fields_set, validation_error = validate_model(cls, value)
, then init model bymodel = cls(**values)
- But I did not set
Hero
modelallow_population_by_field_name = True
, somodel = cls(**values)
can not init correctlly - I have tried set
allow_population_by_field_name = True
onHero
model, then it works,hero
object contains the filedsecret_name
with valuestring
- If I replace
hero: Hero
tohero: HeroPydantic
,v_, errors_ = field.validate(value, values, loc=loc)
can init correctly. But I can't step intopydantic.ModelField.validate
in debugger of pycharm-2022.1.3. So I can't find the reason why pydantic and sqlmodel have different results
@boh5 Do you have solution about this problem? I use the latest version and I still have this problem.
Facing the same issue here
Would be amazing to get this addressed, if possible. It's blocking us from adopting this project.
Looks like there is an open PR to fix this: https://github.com/tiangolo/sqlmodel/pull/774#issuecomment-1883904625
Looks like there is an open PR to fix this: #774 (comment)
I think this PR will only partially fix this issue. It will allow a workaround using validation_alias
but does not address the issue that alias
keyword does not behave the same way as the Pydantic Field alias
when it comes to validating models.
Do we know if this issue is on the radar to be addressed?
Facing a similar issue here, the alias is not taken into account when generating the body for the request.
I am also using pydantic's v2 alias_generator
, and it works if alias
is not defined in the field.
If the alias
is passed, the body doesn't apply the alias_generator
and ignores aliases completely.