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
Heromodel, thesecret_namefield aliased tosecretName - Post data
{
"name": "string",
"secretName": "string",
"age": 0
}
- Then get
hero.secret_nameisNone - If I replace
hero: Herotohero: HeroPydantic, thehero.secret_namecan get correct value from post data. But the strainge thing is that I can get correcthero.secret_namebyhero: 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
heroobject from request body by functionfastapi.dependencies.utils.request_body_to_args, and actually generateheroby this linev_, errors_ = field.validate(value, values, loc=loc) - Then call
sqlmodel.SQLModel.validate. In this function, transform alias dictvalueto field name dictvaluesbyvalues, fields_set, validation_error = validate_model(cls, value), then init model bymodel = cls(**values) - But I did not set
Heromodelallow_population_by_field_name = True, somodel = cls(**values)can not init correctlly - I have tried set
allow_population_by_field_name = TrueonHeromodel, then it works,heroobject contains the filedsecret_namewith valuestring - If I replace
hero: Herotohero: HeroPydantic,v_, errors_ = field.validate(value, values, loc=loc)can init correctly. But I can't step intopydantic.ModelField.validatein 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.