Replies: 12 comments 1 reply
-
|
Yes, that's expected, it's explained here: https://fastapi.tiangolo.com/tutorial/response-model/?h=+response_model_by_alias#response_model_include-and-response_model_exclude That's why |
Beta Was this translation helpful? Give feedback.
-
|
Although this is true for Given Pydantic's design (for example, look at this issue), there is no way to support private (with sunder) attributes without using an alias - and this makes it hard to use such models with sunder attributes in a JSON Schema. |
Beta Was this translation helpful? Give feedback.
-
|
For my case the trick with the second model described here: https://fastapi.tiangolo.com/tutorial/response-model/?h=+response_model_by_alias#response_model_include-and-response_model_exclude worked fine. You can just define a proxy-model with the similar fields, just without the aliases. These models can be used as response model as well as post input schema in FastAPI. Then, you can easily convert the original model by Full example: from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
import uvicorn
app = FastAPI()
class Bar(BaseModel):
bar_bar_1: str = Field(
None, alias="bar:bar-1"
)
bar_bar_2: Optional[str] = Field(
None, alias="bar:bar-2"
)
class Config:
allow_population_by_field_name = True
# Model for OpenAPI schema representation
class BarProxy(BaseModel):
bar_bar_1: str
bar_bar_2: Optional[str]
class Foo(BaseModel):
foo_bar_1: str = Field(
None, alias="foo:bar-1"
)
foo_bar_2: Optional[str] = Field(
None, alias="foo:bar-2"
)
foo_bar_3: Optional[Bar] = Field(
None, alias="foo:bar-3"
)
class Config:
allow_population_by_field_name = True
# Model for OpenAPI schema representation
class FooProxy(BaseModel):
foo_bar_1: str
foo_bar_2: Optional[str]
foo_bar_3: Optional[BarProxy]
# Data consists of full model inclusively field aliases
data = [
Foo(foo_bar_1="field1", foo_bar_2="field2", foo_bar_3=Bar(bar_bar_1="bar_field1", bar_bar_2="bar_field2")),
]
@app.get(
"/foo/{obj_id}",
response_model=FooProxy,
)
def get_foo(obj_id: int):
if obj_id > len(data) - 1 or obj_id < 0:
raise HTTPException(status_code=404, detail="Item not found")
print("Representation of full model:")
print(data[obj_id].dict(by_alias=True))
# With .dict(by_alias=False) FastAPI easily convert the Foo-Model into the FooProxy-Model
return data[obj_id].dict(by_alias=False)
@app.post(
"/foo",
status_code=201,
response_model=FooProxy,
)
def create_foo(obj: FooProxy):
data.append(Foo(**obj.dict()))
print("Representation of full model:")
print(data[-1].dict(by_alias=True))
# With .dict(by_alias=False) FastAPI easily convert the Foo-Model into the FooProxy-Model
return data[-1].dict(by_alias=False)In swagger the model will be represented like this: {
"foo_bar_1": "string",
"foo_bar_2": "string",
"foo_bar_3": {
"bar_bar_1": "string",
"bar_bar_2": "string"
}
} |
Beta Was this translation helpful? Give feedback.
-
|
To me, it is inconsistent if OpenAPI docs shows a different field name that the one expected. Having to create a proxy class to workaround this seems a bit cumbersome. I need to use an alias because I have a field named |
Beta Was this translation helpful? Give feedback.
-
|
Another possible workaround is mapping the values using a root_validator before pydantic does any validation. class Foo(BaseModel):
foo_bar_1: int
foo_bar_2: str
@root_validator(pre=True)
def alias_values(cls, values):
'''
Assigning alias in order for swagger docs to display correct schema keys
'''
values['foo_bar_1'] = values.pop("incoming_key_foo_bar_1")
values['foo_bar_2'] = values.pop("incoming_key_foo_bar_2")
return valuesUsing this allows getting rid of the concept of alias in the schema so FastAPI will simply show the fields you have defined for your schema. This will also maintain the functionality of reading data in by one name and returning it using another. Notice the root_validator has Now there is no need to set response_model_by_alias to False in the endpoint. Another way to improve this is to include a default for the pop value so that rather than a key error coming from values if an incoming key does not exist, you can let pydantic validation find the inconsistency. |
Beta Was this translation helpful? Give feedback.
-
@oji try this: class MyModel(BaseModel):
my_attribute: str = Field(..., alias="myAttribute")
class Config:
allow_population_by_field_name = True
# The schema is generated by default using aliases as keys.
# We want the snake_case name output.
# Modified from:
# - https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization
@staticmethod
def schema_extra(schema: Dict[str, Any], model: Type['Person']) -> None:
snake_case = {}
for field in model.__fields__.values():
k = field.name
v = schema['properties'][field.alias]
snake_case.update({k: v})
schema.update({'properties': snake_case}) |
Beta Was this translation helpful? Give feedback.
-
|
To create a proxy automatically as proposed by lugoll you can use this simple method. def without_alias(model, *, recursively=False):
"""Create a fantom model without alias.
This is used to not exposed internal aliases to fasapi.
Example :
_model.py_
```python
MyModelWithoutAliases = without_alias(MyModel, recursively=True)
```
_router.py_
```python
from model import MyModelWithoutAliases as MyModel
@router.post(..., response_model=MyModel)
```
"""
monkey_model = deepcopy(model)
for name, field in monkey_model.__fields__.items():
if field.alias != name:
field.alias = name
field.field_info.alias = name
field.field_info.alias_priority = 1
if issubclass(field.type_, APIModel) and recursively:
field.type_ = without_alias(field.type_, recursively=recursively)
return monkey_model |
Beta Was this translation helpful? Give feedback.
-
|
Hack. OpenAPI get arg from first AliasChoices. |
Beta Was this translation helpful? Give feedback.
-
|
Honestly, proposed solutions are very cool. However, I genuinely think this is something FastAPI should do. I mean, what is the point of supporting the option to not use serialisation aliases, yet return the wrong schema? Speaking from experience, dealing with schema transformations and custom code like that is very tricky and easy to mess up (I know, I'm dealing with that right now). FastAPI implementing the correct behaviour here (instead of relying on users to provide this) would benefit the whole user base! After all, it is not good design to ask your users to deal with implementation details like that. So please, do the right thing and make FastAPI behave as reasonably expected. Yes, the issue is hard, but pushing the responsibility onto the user is not the right solution. |
Beta Was this translation helpful? Give feedback.
-
|
I agree completely with @Diegovsky Either fix the issue or deprecate the response_model_by_alias option entirely. This middleground is just confusing for users. |
Beta Was this translation helpful? Give feedback.
-
|
Almost a year later, but I have also run into this issue and agree with @Diegovsky and @ePaint. Would be very nice to hear an update on this, or to move this back to an issue such that someone can take it on. |
Beta Was this translation helpful? Give feedback.
-
|
Try this: |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello! I have schema/view like this:
Description
When i accessing route above i receive response like
{'id': 123, 'type': 'smth', 'object_id': 'some_name'}which is correctbut when i open swagger i see in example response
{ "id": 0, "type": "string", "name": "string" # it should be object_id here instead of name }It look like fastapi issue because of
response_model_by_aliasusageEnvironment
Beta Was this translation helpful? Give feedback.
All reactions