Вставить вложенную схему в базу данных с fastAPI? - PullRequest
1 голос
/ 10 апреля 2020

Я недавно узнал о fastAPI и проработал учебник и другие документы. Хотя fastAPI довольно хорошо документирован, я не смог найти информацию о том, как обрабатывать вложенные данные при работе с базой данных.

Для тестирования я написал очень маленькое семейство API с двумя моделями:

class Member(Base):
    __tablename__ = 'members'

    id = Column(Integer, primary_key=True, server_default=text("nextval('members_id_seq'::regclass)"))
    name = Column(String(128), nullable=False)
    age = Column(Integer, nullable=True)
    family_id = Column(Integer, ForeignKey('families.id', deferrable=True, initially='DEFERRED'), nullable=False, index=True)

    family = relationship("Family", back_populates="members")


class Family(Base):
    __tablename__ = 'families'

    id = Column(Integer, primary_key=True, server_default=text("nextval('families_id_seq'::regclass)"))
    family_name = Column(String(128), nullable=False)

    members = relationship("Member", back_populates="family")

, и я создал Postgres базу данных с двумя таблицами и описанными отношениями Вот. С помощью определений схемы и файла crud, как в учебном пособии по fastAPI, я могу создавать отдельные семейства и члены и просматривать их во вложенном виде с помощью запроса get. Вот вложенная схема:

class Family(FamilyBase):
    id: int
    members: List[Member]

    class Config:
        orm_mode = True

Пока все хорошо. Теперь я хотел бы добавить представление поста, которое принимает вложенную структуру в качестве входных данных и соответственно заполняет базу данных. Документация в https://fastapi.tiangolo.com/tutorial/body-nested-models/ показывает, как это сделать в принципе, но она пропускает часть базы данных (то есть crud).

Поскольку входные данные не будут иметь полей id и, очевидно, не Не нужно указывать family_id, у меня есть схема MemberStub и схема NestedFamilyCreate следующим образом:

class MemberStub(BaseModel):
    name: str
    age: int

class NestedFamilyCreate(BaseModel):
    family_name: str
    members: List[MemberStub]

В моей процедуре маршрутизации families.py У меня есть:

@app.post('/nested-families/', response_model=schemas.Family)
def create_family(family: schemas.NestedFamilyCreate, db: Session = Depends(get_db)):
    # no check for previous existence as names can be duplicates
    return crud.create_nested_family(db=db, family=family)

(response_model указывает на вложенное представление семьи со всеми членами, включая все идентификаторы; см. Выше).

Я не могу понять, как написать подпрограмму crud.create_nested_family. На основе простого создания, как в учебном пособии, это выглядит следующим образом:

def create_nested_family(db: Session, family: schemas.NestedFamilyCreate):
    # split information in family and members
    members = family.members
    core_family = None # ??? This is where I get stuck
    db_family = models.Family(**family.dict())  # This fails
    db.add(db_family)
    db.commit()
    db.refresh(db_family)
    return db_family

Итак, я могу извлечь членов и могу через них l oop, но сначала мне нужно будет создать новый db_family запись, которая не должна содержать участников. Затем, с db.refresh, я вернул бы новый family_id, который я мог бы добавить к каждой записи members. Но как я могу это сделать? Если я понимаю, что здесь требуется, мне нужно было бы добиться некоторого отображения моей вложенной схемы в простую схему для FamilyCreate (которая работает сама по себе) и в простую схему для MemberCreate (которая также работает сама по себе). Но как я могу это сделать?

1 Ответ

1 голос
/ 14 апреля 2020

Я нашел решение после перечитывания моделей Pydanti c и их сопоставления с dict.

в crud.py:

def create_nested_family(db: Session, family: schemas.NestedFamilyCreate):
    # split information in family and members
    family_data = family.dict()
    member_data = family_data.pop('members', None)   # ToDo: handle error if no members
    db_family = models.Family(**family_data)
    db.add(db_family)
    db.commit()
    db.refresh(db_family)
    # get family_id
    family_id = db_family.id
    # add members
    for m in member_data:
        m['family_id'] = family_id
        db_member = models.Member(**m)
        db.add(db_member)
        db.commit()
        db.refresh(db_member)
    return db_family

Надеюсь, это может быть полезно для кто-то еще.

...