Flask后端笔记(五)图书小例、数据库迁移、邮件扩展、蓝图

图书小例

在这里插入图片描述

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post">
        {
   
   { form.csrf_token }}

        {
   
   { form.author_name.label }}
        <p>{
   
   {form.author_name}}</p>
        {% for msg in form.author_name.errors %}
            <p>{
   
   {msg}}</p>
        {% endfor %}

        {
   
   { form.book_name.label }}
        <p>{
   
   {form.book_name}}</p>
        {% for msg in form.book_name.errors %}
            <p>{
   
   {msg}}</p>
        {% endfor %}

        {
   
   { form.submit }}
    </form>
    <hr/>
    <ul>
        {% for author in authors %}
        <li>作者:{
   
   { author.name }}</li>
            <ul>
                {% for book in author.books%}
               <li>书籍:{
   
   { book.name }}</li>
                <!--<a href="javascript:;" book-id="{
    
    {book.id}}">删除</a>-->
                <a href="/delete_book?book_id={
     
     {book.id}}">GET删除</a>
                {% endfor %}
            </ul>
        {% endfor %}
    </ul>
    <script type="text/javascript" src="/static/js/jquery.min.js"></script>
    <script>
//        $("a").click(
//            function () {
      
      
//                var data = {
      
      
//                    book_id: $(this).attr("book-id")
//                };
//                // 将js中的对象转换为 json字符串
//                var req_json = JSON.stringify(data);
//    //            $.post("/delete_book", req_json, function (resp) {
      
      
//    //                if (resp.code == 0) {
      
      
//    //                    location.href = "/";
//    //                }
//    //            })
//                $.ajax({
      
      
//                    url: "/delete_book", // 请求的后端url
//                    type: "post",  // 请求方式
//                    data: req_json,  // 向后端发送的请求体数据
//                    contentType: "application/json", // 指明向后端发送的数据格式
//                    dataType: "json",  // 指明后端返回的数据格式
//                    success: function (resp) {
      
      
//                        if (resp.code == 0) {
      
      
//                            alert("oK");
//                            location.href = "/";
//                        }
//                    }
//                })
//            }
//        )
    </script>
</body>
</html>

视图

# coding:utf-8

from flask import Flask, render_template, request, redirect, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired


app = Flask(__name__)


class Config(object):
    # sqlalchemy的配置参数
    SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/author_book_py04"

    # 设置sqlalchemy自动更跟踪数据库
    SQLALCHEMY_TRACK_MODIFICATIONS = True

    SECRET_KEY = "doiso7fd89fyd9^(fsd"


app.config.from_object(Config)

db = SQLAlchemy(app)


# 定义数据库的模型
class Author(db.Model):
    """作者"""
    __tablename__ = "tbl_authors"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    books = db.relationship("Book", backref="author")


class Book(db.Model):
    """书籍"""
    __tablename__ = "tbl_books"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    author_id = db.Column(db.Integer, db.ForeignKey("tbl_authors.id"))


# 创建表单模型类
class AuthorBookForm(FlaskForm):
    """作者数据表单模型类"""
    author_name = StringField(label=u"作者", validators=[DataRequired(u"作者必填")])
    book_name = StringField(label=u"书籍", validators=[DataRequired(u"书籍必填")])
    submit = SubmitField(label=u"保存")


@app.route("/", methods=["GET", "POST"])
def index():
    # 创建表单对象
    form = AuthorBookForm()

    if form.validate_on_submit():
        # 验证表单成功
        # 提取表单数据
        author_name = form.author_name.data
        book_name = form.book_name.data
        # 保存数据库
        author = Author(name=author_name)
        db.session.add(author)
        db.session.commit()

        book = Book(name=book_name, author_id=author.id)
        # book = Book(name=book_name, author=author)
        db.session.add(book)
        db.session.commit()

    # 查询数据库
    author_li = Author.query.all()
    return render_template("author_book.html", authors=author_li, form=form)


# post  /delete_book  json
# {"book_id":x}

# @app.route("/delete_book", methods=["POST"])
# def delete_book():
#     """删除数据"""
#     # 提取参数
#     # 如果前端发送的请求体数据是json格式,get_json会解析成字典
#     # get_json 要求前端传送的数据的Content-Type: application/json
#     req_dict = request.get_json()
#     book_id = req_dict.get("book_id")
#
#     # 删除数据
#     book = Book.query.get(book_id)
#     db.session.delete(book)
#     db.session.commit()
#
#     # "Content-Type": "application/json"
#     return jsonify(code=0, message="OK")


# /delete_book?book_id=xx
@app.route("/delete_book", methods=["GET"])
def delete_book():
    """删除数据"""
    # 提取参数
    book_id = request.args.get("book_id")

    # 删除数据
    book = Book.query.get(book_id)
    db.session.delete(book)
    db.session.commit()

    return redirect(url_for("index"))


if __name__ == '__main__':
    # db.drop_all()
    # db.create_all()
    # au_xi = Author(name='我吃西红柿')
    # au_qian = Author(name='萧潜')
    # au_san = Author(name='唐家三少')
    # db.session.add_all([au_xi, au_qian, au_san])
    # db.session.commit()
    #
    # bk_xi = Book(name='吞噬星空', author_id=au_xi.id)
    # bk_xi2 = Book(name='寸芒', author_id=au_qian.id)
    # bk_qian = Book(name='飘渺之旅', author_id=au_qian.id)
    # bk_san = Book(name='冰火魔厨', author_id=au_san.id)
    # db.session.add_all([bk_xi, bk_xi2, bk_qian, bk_san])
    # db.session.commit()
    app.run(debug=True)

数据库迁移

在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。

更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。

在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。

为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。

首先要在虚拟环境中安装Flask-Migrate。

pip install flask-migrate

文件:database.py

#coding=utf-8
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell,Manager

app = Flask(__name__)
manager = Manager(app)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/Flask_test'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db) 

#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)

#定义模型Role
class Role(db.Model):
    # 定义表名
    __tablename__ = 'roles'
    # 定义列对象
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    def __repr__(self):
        return 'Role:'.format(self.name)

#定义用户
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    def __repr__(self):
        return 'User:'.format(self.username)
if __name__ == '__main__':
    manager.run()

创建迁移仓库

#这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python database.py db init
在这里插入图片描述

创建迁移脚本

自动创建迁移脚本有两个函数,upgrade()函数把迁移中的改动应用到数据库中。downgrade()函数则将改动删除。自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。对比不一定完全正确,有可能会遗漏一些细节,需要进行检查

#创建自动迁移脚本
python database.py db migrate -m 'initial migration'

在这里插入图片描述

更新数据库

python database.py db upgrade

回退数据库

回退数据库时,需要指定回退版本号,由于版本号是随机字符串,为避免出错,建议先使用python database.py db history命令查看历史版本的具体版本号,然后复制具体版本号执行回退。

python database.py db downgrade 版本号
在这里插入图片描述

Flask—Mail

在开发过程中,很多应用程序都需要通过邮件提醒用户,Flask的扩展包Flask-Mail通过包装了Python内置的smtplib包,可以用在Flask程序中发送邮件。

Flask-Mail连接到简单邮件协议(Simple Mail Transfer Protocol,SMTP)服务器,并把邮件交给服务器发送。

在这里插入图片描述
如下示例,通过开启QQ邮箱SMTP服务设置,发送邮件。


from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
#配置邮件:服务器/端口/传输层安全协议/邮箱名/密码
app.config.update(
    DEBUG = True,
    MAIL_SERVER='smtp.qq.com',
    MAIL_PROT=465,
    MAIL_USE_TLS = True,
    MAIL_USERNAME = '[email protected]',
    MAIL_PASSWORD = 'goyubxohbtzfbidd',
)

mail = Mail(app)

@app.route('/')
def index():
 # sender 发送方,recipients 接收方列表
    msg = Message("This is a test ",sender='[email protected]', recipients=['[email protected]','[email protected]'])
    #邮件内容
    msg.body = "Flask test mail"
    #发送邮件
    mail.send(msg)
    print "Mail sent"
    return "Sent Succeed"

if __name__ == "__main__":
    app.run()

不使用蓝图划分模块

将视图函数划分到不同.py文件

循环导入问题

在这里插入图片描述

分别取导入会导致无法完成构建

解决方法一

将导入放入函数中 函数被调入时再进行导入。#解决循环导入的办法:
让一方推迟导入
在这里插入图片描述

解决方法二

注释掉视图函数里的装饰器,main.py中调用装饰器的函数
在这里插入图片描述

蓝图Blueprint

为什么学习蓝图?

我们学习Flask框架,是从写单个文件,执行hello world开始的。我们在这单个文件中可以定义路由、视图函数、定义模型等等。但这显然存在一个问题:随着业务代码的增加,将所有代码都放在单个程序文件中,是非常不合适的。这不仅会让代码阅读变得困难,而且会给后期维护带来麻烦。

什么是蓝图?

蓝图:用于实现单个应用的视图、模板、静态文件的集合。

蓝图就是模块化处理的类。

简单来说,蓝图就是一个存储操作路由映射方法的容器,主要用来实现客户端请求和URL相互关联的功能。 在Flask中,使用蓝图可以帮助我们实现模块化应用的功能。

蓝图的运行机制:

蓝图是保存了一组将来可以在应用对象上执行的操作。注册路由就是一种操作,当在程序实例上调用route装饰器注册路由时,这个操作将修改对象的url_map路由映射列表。当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项。当执行应用对象的 register_blueprint() 方法时,应用对象从蓝图对象的 defered_functions 列表中取出每一项,即调用应用对象的 add_url_rule() 方法,这将会修改程序实例的路由映射列表。

蓝图的使用

一、创建蓝图对象。

#Blueprint必须指定两个参数,admin表示蓝图的名称,__name__表示蓝图所在模块

# 1. 创建蓝图对象
admin = Blueprint('admin',__name__)

二、注册蓝图路由。

# 2. 创建蓝图的视图函数
@admin.route('/')
def admin_index():
    return 'admin_index'

三、在程序实例中注册该蓝图。

# 3.注册蓝图
#第一个参数logins是蓝图对象,url_prefix参数默认值是根路由,如果指定,会在蓝图注册的路由url中添加前缀。访问路径为/admin/
# app.register_blueprint(app_cart)
app.register_blueprint(admin,url_prefix='/admin')

以目录的形式定义蓝图

新建模块包cart
在这里插入图片描述
__init.py

# coding:utf-8

from flask import Blueprint

# 创建一个蓝图 template_folder为模板路径 项目目录下的模板路径优先级 要高于 模块包里的
app_cart = Blueprint("carts", __name__, template_folder="templates")

# 在__init.py文件被执行的时候,把视图加载进来,让蓝图与应用程序知道有视图的存在
from.views import get_cart

view.py

# coding:utf-8

from . import app_cart
from flask import render_template


@app_cart.route("/get_cart")
def get_cart():
    return render_template("cart.html")

main.py

# coding:utf-8

from flask import Flask
from orders import get_order
from users import register
from goods import app_goods
import cart
# from cart import app_cart


# 解决循环导入的办法:
# 让一方推迟导入

app = Flask(__name__)

# 为视图函数添加装饰器,将url路径与视图函数绑定到一起
app.route("/get_order")(get_order)
app.route("/register")(register)

# 注册蓝图
app.register_blueprint(app_goods, url_prefix="/goods")
# app.register_blueprint(app_cart, url_prefix="/carts")
app.register_blueprint(cart.app_cart, url_prefix="/carts")


@app.route("/")
def index():
    return "index page"


if __name__ == '__main__':
    print(app.url_map)
    app.run()


猜你喜欢

转载自blog.csdn.net/qq_27251475/article/details/121016398