У меня есть структура из 3 таблиц
- users
- userFields (ссылается m2o на users, ссылается o2o на userFieldTypes)
- userFieldTypes
Хочу на стороне fastapi получать и отдавать users сразу с его доп полями, и уже внутри по sql распихивать как мне удобно.
Я сделал так, чтобы pydantic модель User можно было менять, вот мои модели для view стороны
class UserFieldType(BaseModel): # create and update
id: Optional[int]
name: str
type: str
@field_validator('type')
@classmethod
def validate_type(cls, value):
if value not in ALLOWED_USER_FIELD_TYPE:
raise ValidationError(f'type must be one of {list(ALLOWED_USER_FIELD_TYPE.keys())}')
return value
class User(BaseModel):
id: Optional[int]
login: str
password: str
description: Optional[str]
# dynamic extra fields
@classmethod
def add_field(cls, name: str, definition: Any, default: Any = ..., optional=True):
if name in cls.model_fields:
raise ValueError(f"Field {name} already exist")
cls.change_field(name, Optional[definition], default, optional)
@classmethod
def change_field(cls, name: str, definition: Any, default: Any = None, optional=True):
if optional:
definition=Optional[definition]
new_field = FieldInfo(annotation=definition, default=default)
cls.model_fields.update({name: new_field})
cls.model_rebuild(force=True)
@classmethod
def delete_field(cls, name: str):
cls.model_fields.pop(name)
cls.model_rebuild(force=True)
И для проверки этого всего сделал несколько маршрутов
@router.get("/users", response_model=(List[schemas.view.User]))
def get_users(
db_session: Session = Depends(session.get_session)
):
return db_session.query(models.User).all()
@router.post("/users", response_model=schemas.view.User)
def add_user(
user: schemas.view.User,
db_session: Session = Depends(session.get_session)
):
print(user)
print('tst' in user)
print(user.tst if 'tst' in user else "")
print(schemas.view.User.model_fields)
return user
@router.get("/user/extra_fields", response_model=(List[schemas.db.UserFieldType]))
def get_user_extra_fields(
db_session: Session = Depends(session.get_session)
):
return db_session.query(models.UserFieldType).all()
@router.post("/user/extra_fields", response_model=schemas.db.UserFieldType)
def add_user_extra_field(
field: schemas.view.UserFieldType,
db_session: Session = Depends(session.get_session)
):
new_field = models.UserFieldType(**field.model_dump())
print(schemas.view.User.model_fields)
schemas.view.User.add_field(field.name, ALLOWED_USER_FIELD_TYPE[field.type])
db_session.add(new_field)
db_session.commit()
print(schemas.view.User.model_fields)
from app.main import app
app.openapi_schema = None
app.setup()
return new_field
Проблема 1:
Я ожидал, что с изменением модели openapi схему надо будет пнуть и для этого у меня app.setup()
, но документация в http:/.../docs'
не обновляется.
Проблема 2:
Даже с учётом того, что я изменил модель User (добавив поле tst
) fastapi её не подхватывает,
т.е. запрос к post("/users")
с данными '{"id": 1, "login": "l1", "password": "p1", "description": "d1", "tst": "t1"}'
выполняется, но в полученном объекте нет поля tst
, эти данные потеряны. А именно я вижу:
print('tst' in user)
->False
print(schemas.view.User.model_fields)
-> вижу своё новое поле{'id': FieldInfo(annotation=Union[int, NoneType], required=True), 'login': FieldInfo(annotation=str, required=True), 'password': FieldInfo(annotation=str, required=True), 'description': FieldInfo(annotation=Union[str, NoneType], required=True), 'tst': FieldInfo(annotation=Union[str, NoneType], required=True)}
Вопрос, что надо правильно пнуть в fastapi чтобы хотябы пройти проблему №2 ?