flask+orm+蓝图

122 阅读4分钟

温馨提示

image.png 用这个命令,仅在这个虚拟环境安装包

1. 目录结构

1.1 根目录新建一个exts.py

把所有的扩展都放这里

例子:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
from exts import db

db.init_app(app)

1.2 根新建一个config.py

DB_USERNAME = 'root'
DB_PASSWORD = '1234'
DB_HOST = '127.0.0.1'
DB_PORT = '3306'
DB_NAME = 'pythonbbs'

DB_URI = 'mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8mb4' % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME)
import config
app.config.from_object(config)

2. 数据库

2.1

pip install flask_migrate

2.2

#`Flask-Migrate` 是一个用于 Flask 应用程序的数据库迁移工具
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
DB_USERNAME = 'root'
DB_PASSWORD = '1234'
DB_HOST = '127.0.0.1'
DB_PORT = '3306'
DB_NAME = 'pythonbbs'

DB_URI = 'mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8mb4' % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME)

SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False

2.3 分模块

新建一个models包 在models包下面新建模块,比如auth.py

pip install pymysql
#是用来实现 **SQLAlchemy 模型对象的序列化功能** 的。它属于 `sqlalchemy-serializer` 库,提供了一种简单的方法将 SQLAlchemy 的模型对象转换为 JSON 或字典格式,方便在 API 中返回模型数据。
pip install sqlalchemy_serializer

可选:

#`shortuuid` 是一个 Python 库,用于生成简短、可读性更高的唯一标识符 (UUID)。
import shortuuid

举例:

from sqlalchemy_serializer import SerializerMixin

from exts import db
import shortuuid
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

class Permission(object):
    # 255的二进制方式来表示 1111 1111
    ALL_PERMISSION = 0b11111111
    # 1. 访问者权限
    VISITOR =        0b00000001
    # 2. 管理板块的权限
    BANNER =        0b00001000
    # 3. 管理前台用户的权限j
    USER =      0b00010000
    # 4. 管理后台管理员的权限
    STAFF =        0b01000000

class RoleModel(db.Model, SerializerMixin):
    serialize_only = ("id", "name", "desc", "create_time")
    __tablename__ = 'role'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)
    desc = db.Column(db.String(200),nullable=True)
    create_time = db.Column(db.DateTime,default=datetime.now)
    permissions = db.Column(db.Integer,default=Permission.VISITOR)
class UserModel(db.Model, SerializerMixin):
    serialize_rules = ("-_password", "-posts", "-comments")
    __tablename__ = "user"
    id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)
    email = db.Column(db.String(50), unique=True, nullable=False)
    username = db.Column(db.String(50), nullable=False)
    _password = db.Column(db.String(200), nullable=False)
    avatar = db.Column(db.String(100))
    signature = db.Column(db.String(100))
    join_time = db.Column(db.DateTime, default=datetime.now)
    is_staff = db.Column(db.Boolean, default=False)
    is_active = db.Column(db.Boolean, default=True)
    role_id = db.Column(db.Integer, db.ForeignKey("role.id"))

    role = db.relationship("RoleModel", backref="users")

    def __init__(self, *args, **kwargs):
        if "password" in kwargs:
            self.password = kwargs.get('password')
            kwargs.pop("password")
        super(UserModel, self).__init__(*args, **kwargs)

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, newpwd):
        self._password = generate_password_hash(newpwd)

    def check_password(self, rawpwd):
        return check_password_hash(self.password, rawpwd)

    def has_permission(self, permission):
        # 当前用户所拥有的权限&permission = permission
        # 0b011 & 0b001 = 0b001
        # 0b011 & 0b100 = 0b000
        return (self.role.permissions & permission) == permission
from flask import Flask

import config
from exts import db
from flask_migrate import Migrate
from models import auth


app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)

migrate = Migrate(app, db)

if __name__ == '__main__':
    app.run()
migrate = Migrate(app, db)
将ORM模型映射到数据库中三部曲
1.初始化迁移仓库:flask db init 
2.将ORM模型生成迁移脚本: flask db migrate
3.运行迁移脚本,生成表:flask db upgrade

3. 蓝图

image.png

from apps.front import front_bp
from flask import g, redirect, url_for
from functools import wraps

def login_required(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if hasattr(g, "user"):
            return func(*args, **kwargs)
        else:
            return redirect(url_for("front.login"))

    return inner

from flask import Blueprint
bp = Blueprint('front', __name__, url_prefix='/')

#可选:
#session登录
from flask_jwt_extended import create_access_token
#分页
from flask_paginate import Pagination
#头像
from flask_avatars import Identicon
#发送邮箱
from flask_mail import Message

#`@bp.before_app_request` 是 **一个请求钩子**,会在每次请求到达任何路由处理函数之前执行。
@bp.before_app_request
def front_before_request():
    if 'user_id' in session:
        user_id = session.get("user_id")
        user = UserModel.query.get(user_id)
        setattr(g, "user", user)

#`@bp.context_processor` 是 **上下文处理器**,用来将一些变量或函数注入到模板上下文中,使得这些变量或函数可以直接在模板中使用。
@bp.context_processor
def front_context_processor():
    if hasattr(g, "user"):
        return {"user": g.user}
    else:
        return {}



from flask import request
from flask_wtf.file import FileAllowed, FileSize

#本身是一个强大的表单处理库
from wtforms import Form,validators
from wtforms.fields import StringField,IntegerField,FileField
from wtforms.validators import Email, Length, EqualTo, ValidationError, InputRequired

from exts import cache
from models.auth import UserModel

class LoginForm(BaseForm):
    email = StringField(validators=[Email(message="请输入正确的邮箱!")])
    password = StringField(validators=[Length(6, 20, message="请输入正确长度的密码!")])
    remember = IntegerField()

4.方法

新建一个utils包

image.png

例子:

# Restful API
from flask import jsonify


class HttpCode(object):
  # 响应正常
  ok = 200
  # 没有登陆错误
  unloginerror = 401
  # 没有权限错误
  permissionerror = 403
  # 客户端参数错误
  paramserror = 400
  # 服务器错误
  servererror = 500


def _restful_result(code, message, data):
  return jsonify({"message": message or "", "data": data or {}, "code": code})


def ok(message=None, data=None):
  return _restful_result(code=HttpCode.ok, message=message, data=data)


def unlogin_error(message="没有登录!"):
  return _restful_result(code=HttpCode.unloginerror, message=message, data=None)


def permission_error(message="没有权限访问!"):
  return _restful_result(code=HttpCode.paramserror, message=message, data=None)


def params_error(message="参数错误!"):
  return _restful_result(code=HttpCode.paramserror, message=message, data=None)


def server_error(message="服务器开小差啦!"):
  return _restful_result(code=HttpCode.servererror, message=message or '服务器内部错误', data=None)

5.注册命令

新建一个commands.py

def init_boards():
    board_names = ['主食', '小食', '甜点', '火锅']
    for index, board_name in enumerate(board_names):
        board = BoardModel(name=board_name, priority=len(board_names)-index)
        db.session.add(board)
    db.session.commit()
    print("板块初始化成功!")
# app.py
app.cli.command("init_boards")(commands.init_boards)
flask init_boards

6.热更

pip install watchdog

watchmedo auto-restart --pattern="*.py" --recursive -- python app.py

7.跨域

pip install flask_cors
from flask_cors import CORS
cors = CORS()
from exts import cors
cors.init_app(app, resources={r"/cmsapi/*": {"origins": "*"}})

8.查询的方法


boards = db.session.query(
    BoardModel.id.label('cid'),
    BoardModel.name.label('label'),
    BoardModel.sele_quantity,

).order_by(BoardModel.priority.desc()).all()
boards_dict = [BoardModel(id=board.cid, name=board.label, sele_quantity=board.sele_quantity).to_dict() for board in boards]


boards = BoardModel.query.order_by(BoardModel.priority.desc()).all()
boards_dict = [board.to_dict() for board in boards]

9.问题总结

  1. tablename = "XXX" 必须为小写,不然最后flask db migrate会出问题!!