官方教程原文

https://fastapi.tiangolo.com/zh/tutorial/sql-databases/

SQLAlchemy的更多操作方法详见
https://www.osgeo.cn/sqlalchemy/orm/tutorial.html

目录结构

官方教程中,main.py放在sql_app文件夹下,考虑到一般习惯,本文将main.py文件放在与sql_app文件夹相同的位置。结构如下:

.
├── main.py
└── sql_app
    ├── __init__.py
    ├── crud.py
    ├── database.py
    ├── models.py
    └── schemas.py

这里__init__.py文件留空即可,它的存在会让解释器将sql_app文件夹视为一个“包”。

数据库连接(database.py)

这里引入SQLAlchemy的主要组件,并初始化数据库连接。

#database.py
from sqlalchemy import create_engine

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import sessionmaker

#数据库访问地址
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

#启动引擎
engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
#启动会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

#数据模型的基类
Base = declarative_base()

数据模型(models.py)

模型(models)用于建立供SQLAlchemy模块增删改查使用的对象,继承SQLAlchemy中的数据模型基类,根据项目需求建立数据模型。

#models.py
from sqlalchemy import Boolean,Column,ForeignKey,Integer,String
from sqlalchemy.orm import relationship

from .database import Base

class User(Base):
    __tablename__="users"

    id=Column(Integer,primary_key=True,index=True)
    email=Column(String,unique=True,index=True)
    hashed_password=Column(String)
    is_active=Column(Boolean,default=True)

    items=relationship('Item',back_populates='owner')



class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("users.id"))
    # 表a_user.items等于相应的Item表
    owner = relationship("User", back_populates="items")

如果需要将模型存储到物理结构,在代码中添加:

Base.metadata.create_all(bind=engine)

这里的Base和engine,就是在上一段代码里面新建的对象,create_all()操作会生成一个*.db结尾的数据库文件并写入表结构,如果数据库文件和表结构已经存在,则不会破坏现有的结构,可以放心使用。

数据模式(schemas.py)

数据模式(schemas)是FastAPI模块用于数据传递的对象,通过继承pydantic中的类建立。

#schemas.py
from typing import List, Optional
from pydantic import BaseModel

class ItemBase(BaseModel):
    title: str
    description: Optional[str] = None


class ItemCreate(ItemBase):

    pass

class Item(ItemBase):
    id: int
    owner_id: int
    class Config:
        orm_mode = True

class UserBase(BaseModel):

    email: str


class UserCreate(UserBase):

    password: str



class User(UserBase):
    id: int
    is_active: bool
    items: List[Item] = []

    class Config:
        orm_mode = True

数据传递(curd.py)

现在,FastAPI和SQLAlchemy两方面均建立了各自的数据模型或模式,接下来需要做的就是定义数据的传递和数据库的增删改查方法。

#curd.py
from sqlalchemy.orm import Session

from . import models, schemas

def get_user(db: Session, user_id: int):
    return db.query(models.User).filter(models.User.id == user_id).first()


def get_user_by_email(db: Session, email: str):
    return db.query(models.User).filter(models.User.email == email).first()


def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.User).offset(skip).limit(limit).all()


def create_user(db: Session, user: schemas.UserCreate):
    fake_hashed_password = user.password + "notreallyhashed"
    db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user



def get_items(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.Item).offset(skip).limit(limit).all()


def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
    db_item = models.Item(**item.dict(), owner_id=user_id)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item

数据查询方法

返回对象为一个字典

这里涉及的类和对象有:

  • db:Session类的对象;
  • User:继承自Base = declarative_base()的类;
  • User.email:User类的成员变量。
db.query(User).filter(User.email == '123@abc.com').first()

相当于

SELECT * FROM users
WHRER email='123@abc.com'
LIMIT 1

数据插入方法

#根据表模型实例化一个数据对象
db_user = models.User(email='123@abc.com', hashed_password="fake_hashed_password")
#向数据库添加数据
db.add(db_user)
#提交更改,只有执行commit()操作才会真正把数据添加到数据库中
db.commit()
#刷新数据库连接
db.refresh(db_user)

数据提取与转化

数据库查询的结果是对象,用点号访问即可,如db_user.email

提取schemas中的数据,可以用点号,如user.email,也可以转化成字典,如user.dict()

相似的模型和模式之间的数据交换可以直接用解包后的字典(如**item.dict())作为构造时的参数。

db_item = models.Item(**item.dict(), owner_id=user_id)

在API 中应用(main.py)

#main.py
from typing import List#用于定义对象数组

from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from sql_app import crud, models, schemas
from sql_app.database import SessionLocal, engine

#在数据库中生成表结构
models.Base.metadata.create_all(bind=engine)


app = FastAPI()


# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)


@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users


@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user


@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
    user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
    return crud.create_user_item(db=db, item=item, user_id=user_id)


@app.get("/items/", response_model=List[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    items = crud.get_items(db, skip=skip, limit=limit)
    return items

代码参数解析

以下面这段代码为例:

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)
  • response_model=schemas.User ,响应模板,限制API返回数据的格式;
  • user: schemas.UserCreate,请求模板,限制前端请求的格式;
  • db: Session = Depends(get_db),注入依赖,执行此函数前先执行函数“get_db”。

启动应用

在命令行输入

$ unicorn main:app --reload

默认端口为8000,运行后可以通过浏览器访问

  • API访问端口: http://127.0.0.1:8000

  • API文档和交互调试: http://127.0.0.1:8000/docs

  • API可选文档:http://127.0.0.1:8000/redoc

  • API原始数据:http://127.0.0.1:8000/openapi.json

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐