flask项目蓝图目录结构:
在蓝图版项目开始之前需要安装数据库同步的插件flask-migrate
项目结构化配置(flask-script蓝图版)
蓝图包初始化
main\__init__.py
from flask import Blueprint # 导入flask蓝图
# 创建蓝图
main = Blueprint("main",__name__)
# 加载视图路由
from . import views
视图路由配置
main\views.py
"""
视图和路由文件
"""
import hashlib
from flask import session
from flask import jsonify
from flask import request
from flask import redirect
from flask import render_template
from . import main # 导入创建的蓝图main
from app import csrf # 导入惰性csrf
from app.models import * # 导入所有模型类
from .forms import TeacherForm # 导入from表单类
def SetPassword(password):
md5 = hashlib.md5()
md5.update(password.encode())
return md5.hexdigest()
def loginValid(fun):
def inner(*args,**kwargs):
cookie_username = request.cookies.get("username")
id = request.cookies.get("user_id")
session_username = session.get("username")
if cookie_username and id and session_username:
if cookie_username == session_username:
return fun(*args,**kwargs)
return redirect("/login/")
return inner
# 使用蓝图注册路由
@main.route("/register/",methods=["GET","POST"])
def register():
if request.method == 'POST':
form_data = request.form
username = form_data.get("username")
password = form_data.get("password")
identity = form_data.get("identity")
user = User()
user.username = username
user.password = SetPassword(password)
user.identity = int(identity)
user.save()
return redirect('/login/')
return render_template("register.html")
@main.route("/login/",methods=["GET","POST"])
def login():
if request.method == "POST":
form_data = request.form
username = form_data.get("username")
password = form_data.get("password")
user = User.query.filter_by(username=username).first()
if user:
db_password = user.password
md5_password = SetPassword(password)
if md5_password == db_password:
# 验证成功,跳转首页
response = redirect('/index/')
# 设置cookie
response.set_cookie("username",username)
response.set_cookie("user_id",str(user.id))
# 设置session
session["username"] = username
# 返回跳转页面
return response
return render_template("login.html")
@main.route("/index/")
@loginValid
def index():
# print(session.get('username'))
response = render_template("index.html",**locals())
return response
@main.route("/logout/",methods=["GET","POST"])
def logout():
response = redirect('/login/')
for key in request.cookies:
response.delete_cookie(key)
del session["username"]
return response
# @csrf.exempt # 临时关闭csrf校验
@main.route("/add_teacher/",methods=["GET","POST"])
def add_teacher():
teacher_form = TeacherForm()
if request.method == "POST":
name = request.form.get("name")
age = request.form.get("age")
gender = request.form.get("gender")
course = request.form.get("course")
teacher = Teacher()
teacher.name = name
teacher.age = age
teacher.gender = gender
teacher.course_id = course
teacher.save()
return render_template("add_teacher.html",**locals())
# csrf 如果没有配置跳转的错误页面
# @csrf.error_handler
@main.errorhandler
@main.route("/csrf_403/")
def csrf_tonken_error(reason):
return render_template("csrf_403.html")
# ajax 前端校验
@main.route("/userValid/",methods=["GET","POST"])
def userValid():
# 定义json字典数据格式
result = {
"code":"",
"data":""
}
if request.method == "POST":
username = request.form.get("username")
if username:
user = User.query.filter_by(username=username).first()
if user:
result["code"] = 400
result["data"] = "用户名已存在"
else:
result["code"] = 200
result["data"] = "用户名未被注册,可以使用"
return jsonify(result)
@main.route("/student_list/")
def student_list():
students = Student.query.all()
return render_template("student_lists.html",**locals())
form表单配置
main\forms.py
import wtforms # 定义字段
from flask_wtf import FlaskForm # 定义表单
from wtforms import validators # 定义校验
# forms当中禁止查看数据库,数据库查询被认为视图功能
course_list = []
class TeacherForm(FlaskForm):
"""
form字段的参数
label=None, 表单的标签
validators=None, 校验,传入校验的方法
filters=tuple(), 过滤
description='', 描述
id=None, html id
default=None, 默认值
widget=None, 样式
render_kw=None, 属性 参数
"""
name = wtforms.StringField(
label="教师姓名",
validators=[
validators.DataRequired('姓名不可以为空')
],
render_kw={
"class": "form-control",
"placeholder": "教师姓名"
}
)
age = wtforms.IntegerField(
label="教师年龄",
validators=[
validators.DataRequired("年龄不可以为空"),
],
render_kw={
"class": "form-control",
"placeholder": "教师年龄",
}
)
gender = wtforms.SelectField(
label="教师性别",
choices=[
("1","男"),
("2","女")
],
render_kw={
"class": "form-control",
}
)
course = wtforms.SelectField(
label="学科",
choices=course_list,
render_kw={
"class": "form-control",
}
)
submit = wtforms.SubmitField(
label="提交",
render_kw={
"class": "btn btn-primary btn-block",
},
)
app应用初始化
app\__init__.py
import pymysql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect # 导入csrf校验模块,csrfProtect在1.0之后移除
pymysql.install_as_MySQLdb()
# 惰性加载
csrf = CSRFProtect()
db = SQLAlchemy()
def create_app(config_name):
# 创建flask App实例
app = Flask(__name__)
# 使用类配置加载
app.config.from_object('settings.DebugConfig')
# app惰性加载插件
csrf.init_app(app)
db.init_app(app)
# 注册蓝图
from .main import main as main_blueprint
from .ApiResource import api_main
app.register_blueprint(main_blueprint)
app.register_blueprint(api_main,url_prefix='/api')
return app
模型类配置
app\models.py
from app import db
# 创建数据库回话,基于会话进行增删改查
# 由于此处session与flask session设置冲突,改为sess
class BaseModel(db.Model):
__abstract__ = True # 抽象表为True,代表当前类为抽象类,不会被创建
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
# 数据保存方法
def save(self):
sess = db.session()
sess.add(self)
sess.commit()
# 数据删除方法
def delete_data(self):
sess = db.session()
sess.delete(self)
sess.commit()
class User(BaseModel):
"""
用户表
"""
__tablename__ = "user"
username = db.Column(db.String(32))
password = db.Column(db.String(32))
identity = db.Column(db.Integer) # 1教室 0学生
identity_id = db.Column(db.Integer,nullable=True)
class Student(BaseModel):
"""
学生表
"""
__tablename__ = "student"
name = db.Column(db.String(32))
age = db.Column(db.Integer)
gender = db.Column(db.Integer) # 1 男 2 女
to_attend = db.relationship(
"Attend",
backref = "to_student"
)
# 学生课程关联表(多对多),也可以使用上面的方式创建该表
Student_Course = db.Table(
"student_course",
db.Column("id",db.Integer,primary_key=True,autoincrement=True),
db.Column("student_id",db.Integer,db.ForeignKey("student.id")),
db.Column("course_id",db.Integer,db.ForeignKey("course.id")),
db.Column("delete_flag",db.Integer,default=1) # 1没有停课 0停课
)
class Course(BaseModel):
"""
课程表
"""
__tablename__ = "course"
name = db.Column(db.String(32))
description = db.Column(db.Text)
to_teacher = db.relationship(
'Teacher', # 映射模型
backref = 'to_course', # 反向映射字段,反向映射表通过该字段查询当前表
) # 映射表字段
to_student = db.relationship(
"Student",# 映射模型
secondary = Student_Course, # 第二映射的模型
backref = db.backref("to_course",lazy="dynamic"),
lazy = "dynamic"
# select 访问该字段时,加载所有的映射数据
# joined 对关联的两个表student和student_course进行join查询
# dynamic 访问该字段时,不加载数据
)
class Grade(BaseModel):
"""
成绩表
学生、课程关联此表
"""
__tablename__ = "grade"
grade = db.Column(db.Float,default=0)
student_id = db.Column(db.Integer,db.ForeignKey("student.id"))
course_id = db.Column(db.Integer,db.ForeignKey("course.id"))
class Attend(BaseModel):
"""
考勤表:记录学生出勤状况
学生考勤:一对多
"""
__tablename__ = "attend"
att_time = db.Column(db.Date)
status = db.Column(db.Integer,default=1) # 0 请假 ;1 正常出勤 ;2 迟到 ;3 早退 ;4 旷课
student_id = db.Column(db.Integer,db.ForeignKey("student.id"))
class Teacher(BaseModel):
"""
老师表
老师与课程是多对一
"""
__tablename__ = "teacher"
name = db.Column(db.String(32))
age = db.Column(db.Integer)
gender = db.Column(db.Integer)
course_id = db.Column(db.Integer,db.ForeignKey("course.id"))
项目配置
settings.py
import os
# from mysql import connector
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
# 通用配置
class BaseConfig():
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY = "admin123"
# 调试配置
class DebugConfig(BaseConfig):
DEBUG = True
SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(BASE_DIR, "Student_test.sqlite") + '?check_same_thread=False'
# SQLALCHEMY_DATABASE_URI = "mysql://root:q1q1q1@localhost/StudentManage"
# SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://root:q1q1q1@localhost/StudentManage"
# 上线配置
class OnlineConfig(BaseConfig):
DEBUG = False
SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(BASE_DIR, "Student.sqlite")
项目启动文件配置
manage.py
from app import create_app,db
from flask_script import Manager
from flask_migrate import Migrate # 用来同步数据库
from flask_migrate import MigrateCommand # 用来同步数据库的命令
# 实例化app
app = create_app("running")
# 命令行封装app
manager = Manager(app)
# 绑定可以管理的数据库模型
migrate = Migrate(app,db)
# 加载数据库管理命令,添加db命令
manager.add_command("db",MigrateCommand)
if __name__ == "__main__":
manager.run()
至此,一个基本的flask蓝图结构及其配置就已经搭建完成,现在就可以测试项目是否有误。
查看项目可以使用的命令
python manage.py
查看db的命令
同步数据库,启动项目
使用flask-migrate的命令
- python manage.py db init 初始化数据库,生成迁移文件
- python manage.py db migrate 同步数据库
- python manage.py upgrade 更新数据库
查看数据库
启动项目
蓝图项目中restful接口的使用
创建一个蓝图包,其下创建一个初始化文件和一个接口视图路由文件
from app.ApiResource import api
from flask_restful import Resource
from app.models import *
result = {
"version": "v1.0",
"code": "",
"data": []
}
@api.resource('/user/')
class UserInfo(Resource):
def get(self):
values = User.query.all()
result["code"] = 200
for value in values:
result["data"].append(
{
value.username:{
"username":value.username,
"password":value.password,
"identity":value.identity,
"is_active":True
}
}
)
result["count"] = len(values)
return result
初始化蓝图:
from flask import Blueprint
from flask_restful import Api
api_main = Blueprint("api_main",__name__)
api = Api(api_main)
from . import ApiResource
app初始化文件中注册接口蓝图:
在这里添加一个参数url_prefix=’/api’配置接口主路由,因为当我们有多个接口视图时,不可能都集中在一个视图路由或者每个接口配置一个根路由