数据工厂系列(3)编写注册接口

197 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

大家好~我是小方,欢迎大家关注笋货测试笔记体完记得俾个like

接上篇

上篇我们讲了后端服务添加日志,日志功能很重要的哦,记录报错信息,分析报错原因,解决报错~今天我们来编写第一个接口--注册接口

创建数据库

之前我们有说过,数据库采用的是MySQL,那么我们先创建数据工厂的数据库。

CREATE DATABASE IF NOT EXISTS datafactory DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

这个命令是指,当datafactory数据库不存在时,建立utf8mb4编码的数据库。

连接数据库配置

先安装sqlalchemyPyMySQL

pip3 install SQLAlchemy==1.4.23
pip3 install PyMySQL==1.0.2

app/models/__init__.py文件引入sqlalchemy

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from config import Config

# Base是用来给模型类继承的
Base = declarative_base()

#创建同步数据库引擎
engine = create_engine(Config.SQLALCHEMY_DATABASE_URI, pool_recycle=7200, pool_pre_ping=True)

#创建会话,autocommit自动提交,autoflush 自动刷新,bind 绑定创建的引擎
Session = sessionmaker(bind=engine)

#向数据库发出建表完成类与表的映射
Base.metadata.create_all(engine)

表设计

先到config.py文件下新建一个权限枚举类

class Permission(object):
    MEMBERS  = 0 # 普通用户
    LEADER = 1  # 组长
    ADMIN = 2  # 超管

app/models下创建user.py

from datetime import datetime
from sqlalchemy import Column, String, INT, DATETIME, SMALLINT, func, Boolean
from app.models import Base
from config import Permission

class DataFactoryUser(Base):
    __tablename__ = "data_factory_user"

    id = Column(INT, primary_key=True, comment="主键id")
    username = Column(String(16), unique=True, nullable=False, comment="用户名")
    name =  Column(String(16), nullable=True, comment="姓名")
    password = Column(String(32), unique=False, comment="密码(md5加密)")
    email = Column(String(128),unique=True, nullable=True, comment="邮箱号")
    role = Column(SMALLINT, default=0, comment="0: 普通用户 1: 组长 2: 超管")
    last_login_time = Column(DATETIME, nullable=True, comment="上次登录时间")
    is_valid = Column(Boolean, nullable=False, default=False, comment="是否冻结")
    create_time = Column(DATETIME, nullable=False, comment="创建时间")
    update_code = Column(String(20), nullable=True, comment="更新人编码")
    update_name = Column(String(20), nullable=True, comment="更新人")
    update_time = Column(DATETIME, onupdate=func.now(), nullable=False, comment="更新时间")

    def __init__(self, username, name, password, email):
        self.username = username
        self.name = name
        self.password = password
        self.email = email
        self.role = Permission.MEMBERS
        self.is_valid = False
        self.create_time = datetime.now()
        self.update_time = datetime.now()
        self.last_login_time = datetime.now()

上面就是用户表的基础字段,用户名、密码、姓名、邮箱号、用户角色(不同角色拥有不同的权限)、是否冻结(封禁账号),其他的都是比较基本的,暂不介绍哈~

注册功能实现

curd目录下新建user目录,接着在user目录新建UserDao.py文件

UserDao.py实现逻辑

from app.models import Session
from sqlalchemy import or_, func
from app.models.user import DataFactoryUser
from app.utils.logger import Log
from config import Permission

class UserDao(object):

    log = Log('UserDao')

    @classmethod
    def register_user(cls, username: str, name: str, password: str, email: str) -> None:
        """
        :param username: 用户名
        :param name: 姓名
        :param password: 密码
        :param email: 邮箱号
        :return:
        """
        try:
            with Session() as session:
                # 先查询用户名或邮箱号是否重复
                users = session.query(DataFactoryUser).filter(or_(DataFactoryUser.username == username, DataFactoryUser.email == email)).first()
                if users:
                    raise Exception('用户名或邮箱号重复')
                # 统计用户表的用户数
                count = session.query(func.count(DataFactoryUser.id)).group_by(DataFactoryUser.id).count()
                user = DataFactoryUser(username, name, password, email)
                # 如果第一个进来,默认是管理员权限
                if count == 0 :
                    user.role = Permission.ADMIN
                session.add(user)
                session.commit()
        except Exception as e:
            cls.log.error(f"用户注册失败: {str(e)}")
            raise Exception(str(e))

编写路由函数

routers目录下新建user目录,接着在user目录新建user.py文件和user_schema.py文件

  • 定义注册接口的schema(user_schema.py)
from pydantic import BaseModel, validator, Field

class RegisterUserBody(BaseModel):

    username: str  = Field(..., title="用户名", description="必传")
    password: str = Field(..., title="密码", description="必传")
    name: str = Field(..., title="姓名", description="必传")
    email: str = Field(..., title="邮箱号", description="必传")

    @validator('username', 'password', 'name', 'email')
    def check_field(cls, v):
        if isinstance(v, str) and len(v.strip()) == 0:
            raise ValueError("不能为空")
        if not isinstance(v, int):
            if not v:
                raise ValueError("不能为空")
        return v
  • 路由函数实现(user.py)
from fastapi import APIRouter
from app.routers.user.user_schema import RegisterUserBody
from app.curd.user.UserDao import UserDao

router = APIRouter()

@router.post('/register', name='用户注册', description='用户注册')
def register(data: RegisterUserBody):
    UserDao.register_user(**data.dict())
    return dict(code=200, msg='注册成功')

注册路由

main.py加入这两段代码

from app.routers.user import user
fun.include_router(user.router, prefix='/api/user', tags=["用户模块"])

测试注册功能

浏览器打开http://127.0.0.1:8080/docs#/swagger文档

请求之后发现服务错误了

原来是数据库中不存在data_factory_user表,解决方法:在main.py文件加入,重启服务即可

from app.curd.user import UserDao
from app.models import Base, engine

Base.metadata.create_all(engine)

data_factory_user表成功创建 再测试一下~

相同的用户名再请求一下

又双出现报错了

我们下期来优化这个痛点~

用户名为空,请求一下

这也太难看了,emmmm也放到下一期优化

总结

今天内容有点多,建议大家慢慢啃下来,上面的代码还需要继续优化,我们先来总结一下有哪些优化点:

  • 抛异常时,没有处理,直接服务器错误了
  • schema校验不通过时,返参太难看了
  • 初始化建表时,需要在main.py文件引入,不太方便
  • 返参没有统一的格式
  • 编写UserDao时,需要手动try...except```比较繁琐...
  • user表的密码明文存储,不太安全
  • 注册router时,需要一个个include_router,太麻烦了
  • ...

好了,今天先到这里了,我们下期见哈~