Flask快速上手

目录

flask 安装插件

项目结构

基本组件

配置文件

第三方插件

创建APP

管理项目

工具集

子应用

模型类

命令

序列化字段

视图

列表、增加

详情、删除、修改

列表分页、增加

列表推导式

详情、删除、修改

分类转移

模糊查询

filter

比较

逻辑

判断

图片

添加图片

图片展示

用户

注册

登录

用户中心

路由

Vue

axios请求拦截器


flask 安装插件

alembic==1.6.2
aniso8601==9.0.1
click==8.0.0
Flask==1.1.2
Flask-Cors==3.0.10
Flask-JWT-Extended==4.2.1
Flask-Migrate==2.5.0
Flask-RESTful==0.3.8
Flask-Script==2.0.6
Flask-SQLAlchemy==2.5.1
greenlet==1.1.0
importlib-metadata==4.0.1
itsdangerous==2.0.0
Jinja2==3.0.0
Mako==1.1.4
MarkupSafe==2.0.0
mysqlclient==2.0.3
PyJWT==2.1.0
pypinyin==0.41.0
python-dateutil==2.8.1
python-editor==1.0.4
pytz==2021.1
six==1.16.0
SQLAlchemy==1.4.15
typing-extensions==3.10.0.0
Werkzeug==2.0.0
zipp==3.4.1


项目结构

flask本身并不提供项目结构,本身只是提供一个最基本的APP。因此,在项目开发时,需要先自己搭建项目:

  • 可以按照功能模块划分为APP,当然,flask中称为蓝图,类似 django,如下所示

  • 可以按照MVC模式划分,即所有的模型类在一起,所有的逻辑处理代码在一起,最后统一配置路由即可。

flaskProject
├── app
│   ├── __init__.py  # 创建APP,挂载所有配置、第三方插件、路由
│   ├── extensions.py  # 配置第三方插件
│   └── settings.py  # 项目基本配置
├── goods  # 蓝图,类似django的app
│   ├── __init__.py 
│   ├── models.py  # 创建模型类
│   ├── ser.py  # 指定模型的序列化字段
│   ├── urls.py  # 对每个视图配置路由
│   └── views.py  # 视图处理
├── manage.py  # 项目的管理文件
└── sqlite.db  # 数据库

基本组件

配置文件

app/settings.py

import datetime
import os


class Config(object):
    DEBUG = True  # 调试模式
    SQLALCHEMY_DATABASE_URI = 'mysql://root:mysql@127.0.0.1:3306/day08'  # 数据库的地址
    SQLALCHEMY_TRACK_MODIFICATIONS = False  # 默认不追踪 mysql修改的信号量
    SECRET_KEY = b'\xe6O$\xe8\t\xa5\x7f\xf4\x01#7K\x03\x91\x94d'  # 指明加密的密钥

    # 构建项目所在的 绝对路径,也就是 day08 的绝对路径
    BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    # 静态资源存放路径
    STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
    # 自定义的 图片上传路径
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

    JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(days=1)  # 指明token的过期时间
    # JWT_HEADER_TYPE = 'JWT'  # 可以修改 请求头中 ,token字符串的 前缀
    # 默认   Authorization: Bearer <token>
    # 修改:  Authorization: JWT <token>

第三方插件

flask项目中,需要使用flask_sqlalchemy数据库管理工具、 flask_migrate模型类迁移工具

app/extensions.py

from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import JWTManager
# import pymysql

# pymysql.install_as_MySQLdb()  
# 使用mysql数据库时,要么使用pymysql,然后这么配置
# 要么直接使用 mysqlclient

db = SQLAlchemy()  # 创建db对象,方便后期模型类的创建
migrate = Migrate()  # 创建迁移对象
cors = CORS()
jwt = JWTManager()


def config_extensions(app):
    """定义方法,在app初始化时,传入app参数,挂载到app上"""
    db.init_app(app)
    migrate.init_app(app,db=db)
    cors.init_app(app)
    jwt.init_app(app)

创建APP

一个 Flask 应用是一个 Flask 类的实例。应用的所有东西(例如配置 和 URL )都会和这个实例一起注册。

创建一个 Flask 应用最粗暴直接的方法是在代码的最开始创建一个全局 Flask 实例。有的情况下这 样做是简单和有效的,但是当项目越来越大的时候就会有些力不从心了。

可以在一个函数内部创建 Flask 实例来代替创建全局实例。这个函数被 称为 应用工厂 。所有应用相关的配置、注册和其他设置都会在函数内部完成, 然后返回这个应用。

app/__init__.py

from flask import Flask

from app.settings import config
from extensions import config_extensions
# 后期创建完模型类,导入到当前文件,让 app管理
# from goods.models import *
# 后期对视图配置路由之后,导入路由
# from goods.urls import *

def create_app(config_name=None):
    app = Flask(__name__)
    # 导入配置文件
    app.config.from_object(config)
    # 初始化工具(sqlalchemy,migrate)
    config_extensions(app)

    return app

管理项目

如果是单独的app文件,直接运行即可。当项目庞大时,可以借助 flask_script来管理项目

manage.py

import os
from flask_migrate import MigrateCommand
from flask_script import Manager, Server
from app import create_app

config_name = os.getenv('FLASK_CONFIG' , 'default')

app = create_app(config_name)

manage = Manager(app)

manage.add_command('runserver', Server(host='localhost', port=5000))
manage.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manage.run()

工具集

自定义函数,将图片保存到本地

import os
from uuid import uuid4
from flask import current_app  # current_app 属于应用上下文,代表项目中的app本身


def img_upload(img):
    if not img:
        return None

    # 将图片名按照 .  进行切分, 找到最后一个元素,也就是  文件的后缀名
    end_name = img.filename.rsplit('.')[-1]

    # 通过文件的后缀名判断 身份为 合法的  图片
    if end_name not in ['jpg', 'png', 'gif', 'jpeg']:
        return None

    # 将 图片对象 存入 本地,然后将 路径 存入 数据库
    MEDIA = current_app.config['MEDIA_ROOT']  # 从app的配置项,取出 MEDIA的路径
    filename = str(uuid4()) + '.' + end_name  # 为了生成一个不重复的 文件名
    img_path = os.path.join(MEDIA, filename)  # 将路径和文件名拼接在一起,方便保存文件

    img.save(img_path)  # 将图片对象保存到 本地

    return filename

子应用

在项目中创建一个包:school

school/__init__.py: 创建蓝图对象, 创建完成,需要 注册蓝图

from flask.blueprints import Blueprint


school_bp = Blueprint('school', __name__)


# 为了让蓝图,管理你的 模型类和视图,因此需要导入
from .models import *
from .views import *

模型类

from app.extensions import db


class Cate(db.Model):
    __tablename__ = 'tb_cate'
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(10))
    
    # 外键,关联自身的主键
    parent_id = db.Column(db.Integer, db.ForeignKey('tb_cate.id'), nullable=True)
    
    # 指明关联字段,和实质的表无关系,只是在查询时,实现关联查询
    parent = db.relationship('Cate', remote_side=[id], backref="sub")

    def __repr__(self):
        return 'Cate: %s' % self.name


class Goods(db.Model):
    __tablename__ = 'tb_goods'
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(50))
    price = db.Column(db.DECIMAL(10, 2))
    stock = db.Column(db.Integer, default=0)
    sales = db.Column(db.Integer, default=0)
    
    # 外键,关联分类表的主键
    cate_id = db.Column(db.Integer, db.ForeignKey('tb_cate.id'))
    
    # 指明关联字段,和实质的表无关系,只是在查询时,实现关联查询
    cate = db.relationship('Cate', backref='goods')

    def __repr__(self):
        return 'Goods: %s' % self.name

命令

在项目的终端可以使用如下命令,操作项目

python manage.py runserver  # 启动flask 服务


python manage.py db init # 只有第一次迁移时,才会执行 该命令, 执行完,会生成迁移文件
python manage.py db migrate  # 生成迁移文件,出路第一次之外,任何修改模型类,都需要 执行该命令, 生成迁移文件

python manage.py  db upgrade  # 根据迁移文件,生成表

序列化字段

在序列化字段中,嵌套 列表字典,不能直接嵌套,需要使用 ListNested

goods/ser.py

from flask_restful import fields

cate_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'parent': fields.String(attribute='parent.name'),
}

cate2_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'sub': fields.List(fields.Nested(cate_fields))
}

cate1_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'sub': fields.List(fields.Nested(cate2_fields))
}

goods_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'stock': fields.Integer,
    'sales': fields.Integer,
    'cate': fields.Nested({
        'id': fields.Integer,
        'name': fields.String
    }),
}

goods_page_fields = {
    'data': fields.Nested(goods_fields),
    'total': fields.Integer,
    'pages': fields.Integer
}

视图

goods/views.py

列表、增加

from flask_restful import Resource, reqparse, marshal_with
from .models import *
from .ser import *


class CateView(Resource):
    parser = reqparse.RequestParser()

    @marshal_with(cate_fields)
    def post(self):
        # 1. 解析校验参数
        self.parser.add_argument('name', type=str, required=True, location=['form', 'json'])
        self.parser.add_argument('parent_id', type=int, location=['form', 'json'])
		
        # 2. 获取参数
        args = self.parser.parse_args()

        # 2.1 校验父类是否存在
        parent_id = args.get('parent_id')

        if parent_id:
            Cate.query.get_or_404(parent_id)
		
        # 3. 添加数据
        cate = Cate(**args)

        # 4. 添加事务
        db.session.add(cate)
        
        # 5. 提交事务
        db.session.commit()
		# 6. 按照序列化字段解析分类对象,并返回响应
        return cate, 201
	
    # 实现三级分类解析
    @marshal_with(cate1_fields)
    def get(self):
        cates = Cate.query.all()
        return cates, 200

详情、删除、修改

from flask_restful import Resource, reqparse, marshal_with
from .models import *
from .ser import *


class CateDetailView(Resource):
    parser = reqparse.RequestParser()

    @marshal_with(cate_fields)
    def get(self, pk):
        cate = Cate.query.get_or_404(pk)
        return cate, 200

    def delete(self, pk):
        cate = Cate.query.get_or_404(pk)
        db.session.delete(cate)
        db.session.commit()

        return None, 204

    @marshal_with(cate_fields)
    def put(self, pk):
        cate = Cate.query.get_or_404(pk)
        self.parser.add_argument('name', type=str, location=['form', 'json'], default=cate.name)
        self.parser.add_argument('parent_id', type=int, location=['form', 'json'], default=cate.parent_id)

        name = self.parser.parse_args().get('name')
        parent_id = self.parser.parse_args().get('parent_id')

        cate.name = name
        cate.parent_id = parent_id

        db.session.commit()

        return cate, 201

列表分页、增加

from flask_restful import Resource, reqparse, marshal_with
from .models import *
from .ser import *


class GoodsView(Resource):
    parser = reqparse.RequestParser()

    @marshal_with(goods_fields)
    def post(self):
        self.parser.add_argument('name', type=str, location=['form', 'json'], required=True)
        self.parser.add_argument('price', type=float, location=['form', 'json'], required=True)
        self.parser.add_argument('stock', type=int, location=['form', 'json'], default=1000)
        self.parser.add_argument('sales', type=int, location=['form', 'json'], default=0)
        self.parser.add_argument('cate_id', type=int, location=['form', 'json'], required=True)

        args = self.parser.parse_args()

        goods = Goods(**args)

        db.session.add(goods)
        db.session.commit()

        return goods, 201

    @marshal_with(goods_page_fields)
    def get(self):
        self.parser.add_argument('cate_id', type=int, location='args')
        self.parser.add_argument('page', type=int, default=1, location='args')
        self.parser.add_argument('size', type=int, default=2, location='args')

        args = self.parser.parse_args()
        cate_id = args.get('cate_id')
        page = args.get('page')
        size = args.get('size')

        pagination = Goods.query.filter_by(cate_id=cate_id).paginate(page, size, error_out=False)  # 分页实现

        # 注意:返回的数据结构需要和序列化字段结构一样
        return {
                   "data": pagination.items, # 当前页的数据
                   'total': pagination.total, # 总的记录数
                   'pages': pagination.pages  # 分页后的总页码
               }, 200

列表推导式

from flask_restful import Resource, reqparse
from sqlalchemy import desc
from .models import *


class StuView(Resource):
    def get(self):
        # 1. 创建解析参数的对象
        parser = reqparse.RequestParser()
        # 2. 指明需要解析的参数
        parser.add_argument('page', type=int, location='args', default=1, help='页码不合法')

        # 3. 获取参数, 得到 字典 { 参数名: 参数值}
        args = parser.parse_args()
        # 4. 从字典中取出具体的 参数
        page = args.get('page')

        # 5. 先查询,后排序,再分页, 得到分页器对象
        pagination = Stu.query.order_by(desc('addtime')).paginate(page=page, per_page=2)

        # 6. 为了解析时间格式,还是 手动解析
        stus = [
            {
                'id': stu.id,
                'name': stu.name,
                'gender': '男' if stu.gender else '女',
                'age': stu.age,
                'state': stu.state,
                'cls': stu.cls.name,
                'add_time': stu.addtime.strftime('%Y-%m-%d %H:%M:%S')
            }

            for stu in pagination.items
        ]

        # 7. 返回响应数据
        return {
            'stus': stus,
            'pages': pagination.pages
        }

详情、删除、修改

from flask_restful import Resource, reqparse, marshal_with
from .models import *
from .ser import *


class GoodsDetailView(Resource):
    parser = reqparse.RequestParser()

    @marshal_with(goods_fields)
    def get(self, pk):
        goods = Goods.query.get_or_404(pk)
        return goods, 200

    def delete(self, pk):
        goods = Goods.query.get_or_404(pk)
        db.session.delete(goods)
        db.session.commit()

        return None, 204

    @marshal_with(goods_fields)
    def put(self, pk):
        goods = Goods.query.get_or_404(pk)
        self.parser.add_argument('name', type=str, location=['form', 'json'], default=goods.name)
        self.parser.add_argument('price', type=float, location=['form', 'json'], default=goods.price)
        self.parser.add_argument('stock', type=int, location=['form', 'json'], default=goods.stock)
        self.parser.add_argument('sales', type=int, location=['form', 'json'], default=goods.sales)
        self.parser.add_argument('cate_id', type=int, location=['form', 'json'], default=goods.cate_id)

        name = self.parser.parse_args().get('name')
        price = self.parser.parse_args().get('price')
        stock = self.parser.parse_args().get('stock')
        sales = self.parser.parse_args().get('sales')
        cate_id = self.parser.parse_args().get('cate_id')
        
        cate = Cate.query.get_or_404(cate_id)

        goods.name = name
        goods.cate_id = cate_id
        goods.price = price
        goods.stock = stock
        goods.sales = sales

        db.session.commit()

        return goods, 201

分类转移

from flask_restful import Resource, reqparse, marshal_with
from .models import *
from .ser import *


class GoodsCateChangeView(Resource):
    parser = reqparse.RequestParser()

    @marshal_with(goods_fields)
    def put(self, pk):
        self.parser.add_argument('cate_id', type=int, required=True, location=['form', 'json'])
        cate_id = self.parser.parse_args().get('cate_id')
        cate = Cate.query.get_or_404(cate_id)
        goods = Goods.query.get_or_404(pk)
        goods.cate_id = cate.id
        db.session.commit()

        return goods, 201

模糊查询

class SearchView(Resource):
    def get(self):
        # 1. 创建解析参数的对象
        parser = reqparse.RequestParser()
        # 2. 指明需要解析的参数
        parser.add_argument('text', type=str, location='args', help='关键字不合法')

        # 3. 获取参数, 得到 字典 { 参数名: 参数值}
        args = parser.parse_args()
        # 4. 从字典中取出具体的 参数
        text = args.get('text')

        # 5. 根据关键字模糊查询, 得到学生的 查询集
        stus = Stu.query.join(Cls, Cls.id == Stu.cls_id). \
            filter(or_(
            Cls.name.like('%{}%'.format(text)),
            Stu.name.like('%{}%'.format(text))
        )).all()

        # 6. 循环解析
        data = [
            {
                'id': stu.id,
                'name': stu.name,
                'gender': '男' if stu.gender else '女',
                'age': stu.age,
                'state': stu.state,
                'cls': stu.cls.name,
                'add_time': stu.addtime.strftime('%Y-%m-%d %H:%M:%S')
            }

            for stu in stus
        ]

        return {
            'stus': data
        }

filter

比较

filter中支持 python中的所有比较运算符: ==!=><>=<=

from flask_restful import Resource, reqparse, marshal_with
from .models import Goods
from .fields import goods_fields


class GoodsView(Resource):

    @marshal_with(goods_fields)
    def get(self):
        # 1. 客户端传递 参数,
        parser = reqparse.RequestParser()
        parser.add_argument('sales', type=int, location=['args'], default=0)
        
        sales = parser.parse_args().get('sales')
        

        # 2. 查询销量大于该参数的所有商品
        goods = Goods.query.filter(Goods.sales > sales).all()
        return goods

注意: 过滤完成之后,还需要调用 all()获取 查询的 结果


逻辑

from flask_restful import Resource, reqparse, marshal_with
from .models import Goods
from .fields import goods_fields
from sqlalchemy import or_, and_, not_


class GoodsView(Resource):

    @marshal_with(goods_fields)
    def get(self):
        # 1. 客户端传递 参数,

        parser = reqparse.RequestParser()
        parser.add_argument('sales', type=int, location=['args'], default=0)
        parser.add_argument('stock', type=int, location=['args'], default=0)

        sales = parser.parse_args().get('sales')
        stock = parser.parse_args().get('stock')

        # 2. 查询销量大于 某个数 或  库存小于 某个数 的商品信息
        goods = Goods.query.filter(or_(Goods.sales > sales, Goods.stock < stock)).all()

        return goods
from flask_restful import Resource, reqparse, marshal_with
from .models import Goods
from .fields import goods_fields
from sqlalchemy import or_, and_, not_


class GoodsView(Resource):
    @marshal_with(goods_fields)
    def get(self):
        # 1. 客户端传递 参数
        parser = reqparse.RequestParser()
        parser.add_argument('max_price', type=float, location=['args'])
        parser.add_argument('min_price', type=float, location=['args'])

        max_price = parser.parse_args().get('max_price')
        min_price = parser.parse_args().get('min_price')

        if (max_price is None) and (min_price is None):
            # 上限  和  下限  都没有
            goods = Goods.query.all()
            
        elif (max_price is None) and (min_price is not None):
            # 有 下限 ,没有 上限
            goods = Goods.query.filter(Goods.price >= min_price).all()
            
        elif (max_price is not None) and (min_price is None):
            # 有 上限 ,没有 下限
            goods = Goods.query.filter(Goods.price <= max_price).all()
            
        else:
            # 查询 价格在某个区间的商品信息
            goods = Goods.query.filter(and_(Goods.price >= min_price, Goods.price <= max_price)).all()

        return goods
  • 模型类.query.filter(模型类.字段 > 值).all(): 比较查询,支持所有比较运算符

  • 模型类.query.filter(or_(条件1, 条件2)).all(): 满足任何一个条件即可

  • 模型类.query.filter(and_(条件1, 条件2)).all(): 满足全部条件即可

  • 模型类.qery.filter(模型类.字段.in_([1, 2, 3, 4])): 查询某个字段是否 在某个 集合中,用来实现 批量操作


判断

# 查询库存不为空 的商品信息
goods = Goods.query.filter(not_(Goods.stock.is_(None))).all()
goods = Goods.query.filter(~(Goods.stock.is_(None))).all()


# id在某个区间的商品信息
goods = Goods.query.filter(Goods.id.in_([1, 3, 6, 7])).all()

图片

添加图片

from flask import current_app, make_response

from sqlalchemy import desc
from werkzeug.datastructures import FileStorage
from flask_restful import Resource, reqparse, marshal_with
from .models import *
from utils.img import img_upload
import os


class NewsView(Resource):
    def post(self):
        # 1. 创建解析参数的对象
        parser = reqparse.RequestParser()
        # 2. 指明需要解析的参数
        parser.add_argument('title', type=str, location='form', required=True)
        parser.add_argument('content', type=str, location='form')
        parser.add_argument('count', type=int, location='form', default=0)
        parser.add_argument('cate_id', type=int, location='form', required=True)
        parser.add_argument('img', type=FileStorage, location='files')

        # 3. 获取具体的参数
        args = parser.parse_args()

        title = args.get('title')
        content = args.get('content')
        count = args.get('count')
        cate_id = args.get('cate_id')
        img = args.get('img')

        # 利用自定义函数,将图片保存到本地
        filename = img_upload(img)

        # 4. 创建对象, 注意:图片存储的只是 从media之后的  图片路径
        news = News(title=title, content=content, count=count, img=filename, cate_id=cate_id)
        # 5. 添加到 事务中
        db.session.add(news)
        # 6. 提交事务
        try:
            db.session.commit()
        except:
            return {
                       'msg': '添加失败'
                   }, 500
        # 7. 返回响应
        return {
                   'id': news.id,
                   'title': news.title,
                   'img': news.img
               }, 201

图片展示

图片展示,实际就是 将 图片内容读取为文件流,做为响应返回

http://127.0.0.1:5000/media/filename

方案一:

class ImgView(Resource):
    def get(self, filename):
        # 1. 从配置项中读取 media目录的 路径
        MEDIA = current_app.config['MEDIA_ROOT']
        # 2. 拼接除图片的完成路径
        img_path = os.path.join(MEDIA, filename)

        # 3. 按照二进制方式打开文件,读到的内容为 二进制文件流,方便接下来的网络传输
        try:
            with open(img_path, 'rb') as f:
                img = f.read()
        except FileNotFoundError:
            return None, 404

        # 4. 自定义响应
        resp = make_response(img)
        # 5. 声明响应体的类型 为  图片
        resp.headers['Content-Type'] = 'image/png'
        # 6. 返回响应
        return resp

方案二:

class ImgView(Resource):
    def get(self, filename):
        return send_from_directory(Config.MEDIA_ROOT, filename)

用户

注册

from flask_restful import Resource, reqparse
from .models import *
from werkzeug.security import generate_password_hash, check_password_hash
from flask_jwt_extended import create_access_token,jwt_required
import re


class RegisterView(Resource):
    def post(self):
        # 1. 创建解析参数对象
        parser = reqparse.RequestParser()
        # 2. 指明需要解析的参数
        parser.add_argument('username', type=str, location=['form', 'json'], required=True, help='输入合法的用户名')
        parser.add_argument('password', type=str, location=['form', 'json'], required=True, help='输入安全的密码')
        # 3. 获取解析后的参数
        username = parser.parse_args().get('username')
        password = parser.parse_args().get('password')

        # 3.1 校验  用户名规则: 字母、数字、_组成, 字母开头,6-20位
        if not re.match(r'^[a-zA-Z]\w{5,19}$', username):
            return {
                       'msg': '用户名不合法'
                   }, 400

        # 3.2 校验用户名是否重复
        if User.query.filter_by(username=username).count():
            return {
                'msg': '该用户名已存在'
            }
        
        # 3.3 校验 密码规则:  必须存在  数字、 大写字母、小写字母, 8-20位
        if not re.match(r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,20}$', password):
            return {
                       'msg': '密码不安全'
                   }, 400

        # 4. 对密码进行加密
        new_password = generate_password_hash(password)
        # 5. 创建用户对象
        user = User(username=username, password=new_password)
        # 6. 将用户对象添加到 事务中
        db.session.add(user)
        # 7. 提交事务
        db.session.commit()
        # 8. 注册成功
        return {
            'msg': '用户注册成功'
        }

登录

登录成功,需要 生成 jwt字符串

  • 在 flask扩展项中,挂载 flask_jwt_extend 插件

  • 在 项目的 配置中,指明 SECRET_KEY 或者 JWT_SECRET_KEY, 密钥可以使用 os.urandom(n), 指明生成固定长度的密钥

  • 在 项目的 配置中,指明 JWT_ACCESS_TOKEN_EXPIRES, 也就是 token的过期时间

class LoginView(Resource):
    def post(self):
        # 1. 创建解析参数对象
        parser = reqparse.RequestParser()
        # 2. 指明需要解析的参数
        parser.add_argument('username', type=str, location=['form', 'json'], required=True, help='输入合法的用户名')
        parser.add_argument('password', type=str, location=['form', 'json'], required=True, help='输入安全的密码')
        # 3. 获取解析后的参数
        username = parser.parse_args().get('username')
        password = parser.parse_args().get('password')

        # 4. 需要从数据库中查询 得到 用户对象
        user = User.query.filter_by(username=username).first()

        # 5. 如果用户不存在,返回错误信息
        if not user:
            return {
                       'msg': '用户名或密码错误'
                   }, 401

        # 6. 校验 密码
        if not check_password_hash(user.password, password):
            return {
                       'msg': '用户名或密码错误'
                   }, 401

        # 7. 生成token
        token = create_access_token(identity={'id': user.id, 'username': user.username})

        # 8. 返回token
        return {
            'token': token,
            'usernamne': user.username
        }

用户中心

通过 请求头中 的 token进行身份认证, 使用 默认提供的 装饰器进行身份认证

from flask_restful import Resource, reqparse
from flask_jwt_extended import jwt_required, get_jwt_identity

class UserInfoView(Resource):
    method_decorators = [jwt_required()]

    def get(self):
        # 1. 获取token中的 载荷信息, 也就是 上一步登录成功。生成token时,存入 token的信息
        user = get_jwt_identity()

        return user

路由

goods/urls.py

from app.extensions import api
from .views import *

api.add_resource(CateView, '/cate/')
api.add_resource(CateDetailView, '/cate/<int:pk>/')
api.add_resource(GoodsView, '/goods/'),
api.add_resource(GoodsDetailView, '/goods/<int:pk>/'),
api.add_resource(GoodsCateChangeView, '/goods/<int:pk>/cate/'),
api.add_resource(ImgView, '/media/<string:filename>')

Vue

axios请求拦截器

登录成功,Vue保存 token,但是下次请求时,需要在请求头中携带 token

  • 可以在每次请求时,指明token

  • 可以在 main.js配置 axios请求拦截器,全局配置一次即可

axios.defaults.baseURL = 'http://127.0.0.1:5000/'
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么, 在发送请求之前,从本地读取 token,放到 请求头中
  config.headers.Authorization = 'JWT ' + sessionStorage.getItem('token')
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});

Vue.prototype.$axios = axios

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向晚-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值