Insert a nested schema into a database with fastAPI?

mas*_*chu 2 python-3.x pydantic fastapi

I have recently come to know about fastAPI and worked my way through the tutorial and other docs. Although fastAPI is pretty well documented, I couldn't find information about how to process a nested input when working with a database.

For testing, I wrote a very small family API with two models:

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")
Run Code Online (Sandbox Code Playgroud)

and I created a Postgres database with two tables and the relations described here. With schema definitions and a crud file as in the fastAPI tutorial, I can create individual families and members and view them in a nested fashion with a get request. Here is the nested schema:

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

    class Config:
        orm_mode = True
Run Code Online (Sandbox Code Playgroud)

So far, so good. Now, I would like to add a post view which accepts the nested structure as input and populates the database accordingly. The documentation at https://fastapi.tiangolo.com/tutorial/body-nested-models/ shows how to do this in principle, but it misses the database (i.e. crud) part.

As the input will not have id fields and obviously doesn't need to specify family_id, I have a MemberStub schema and the NestedFamilyCreate schema as follows:

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

class NestedFamilyCreate(BaseModel):
    family_name: str
    members: List[MemberStub]
Run Code Online (Sandbox Code Playgroud)

In my routing routine families.py I have:

@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)
Run Code Online (Sandbox Code Playgroud)

(the response_model points to the nested view of a family with all members including all ids; see above).

What I cannot figure out is how to write the crud.create_nested_family routine. Based on the simple create as in the tutorial, this looks like:

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
Run Code Online (Sandbox Code Playgroud)

So, I can extract the members and can loop through them, but I would first need to create a new db_family record which must not contain the members. Then, with db.refresh, I would get the new family_id back, which I could add to each record of members. But how can I do this? If I understand what is required here, I would need to achieve some mapping of my nested schema onto a plain schema for FamilyCreate (which works by itself) and a plain schema for MemberCreate (which also works by itself). But how can I do this?

mas*_*chu 6

在重新阅读 Pydantic 模型及其到 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
Run Code Online (Sandbox Code Playgroud)

希望,这可能对其他人有用。