• 【Flask扩展】Flask-Script、Flask-SQLAlchemy、Flask-Migrate、Flask-RESTful


    好风凭借力,送我上青云。

    Flask-SQLAlchemy

    安装

    pip install flask-sqlalchemy -i https://pypi.doubanio.com/simple
    

    定义使用

    Flask-SQLAlchemy和原生的SQLAlchemy基本一致,首先想让我们定义一下数据库并进行连接

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)  # type:Flask
    
    HOSTNAME = '127.0.0.1'
    PORT = '3306'
    DATABASE = 'flask'
    USERNAME = 'root'
    PASSWORD = 'root'
    DB_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"
    
    app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
    db = SQLAlchemy(app=app)
    
    
    class User(db.Model):
        __tablename__ = 'user'
    
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(length=50), nullable=False)
    
    
    class Article(db.Model):
        __tablename__ = 'article'
    
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(length=50), nullable=False)
    
        uid = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    
        author = db.relationship("User", backref="articles")
    
    
    # db.drop_all()
    # db.create_all()
    
    # user = User(username="ydy")
    # article = Article(title="xxxxxxaaaa")
    #
    # article.author = user
    #
    # db.session.add(article)
    # db.session.commit()
    
    users = User.query.all()
    print(users)
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    使用Flask-SQLAlchemy需要配置一下几个部分:

    1. 导入from flask_sqlalchemy import SQLAlchemy
    2. 配置相关数据库参数用于连接,最终形成一个URI:DB_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"
    3. 把URI添加到app的配置,app.config['SQLALCHEMY_DATABASE_URI'] = DB_URIapp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False为了防止结合alembic生成迁移数据库提示错误信息
    4. 创建SQLAlchemy对象,db = SQLAlchemy(app=app),这一步必须放在配置之后否则无效。

    结合alembic迁移

    在对应文件目录执行命令,初始化一个alembic的文件夹(名称随便取,一般为alembic)

    alembic init alembic
    

    文件目录如下:

    .
    ├── alembic
    │   ├── env.py
    │   ├── README
    │   ├── script.py.mako
    │   └── versions
    ├── alembic.ini
    └── demo.py
    

    修改alembic/env.py文件配置

    目的是导入我们app所在文件的模块路径。需要根据个人情况,每个人的目录结构不同,所以查找路径不同。例如:我的文件就是上面的demo.py,结合目录结构。之后还需要导入app所在的文件,并且修改target_metadata的值

    import sys
    import os
    sys.path.append(os.path.dirname(os.path.dirname(__file__)))
    
    import demo
    
    target_metadata = demo.db.Model.metadata
    

    修改alembic.ini配置

    sqlalchemy.url = mysql+pymysql://root:root@localhost/demo?charset=utf8
    

    生成迁移脚本

    alembic revision --autogenerate -m 'message'
    

    成功之后会在versions文件夹下创建一个py文件。

    基本的Flask-SQLALchemy配置完成,其他的操作基本和SQLAlchemy一致。

    Flask-Script

    安装

    pip install flask-script -i https://pypi.doubanio.com/simple
    

    定义使用

    新建一个文件manager.py,输入一下格式代码

    from flask_script import Manager
    from xxx import app # 从其他文件导入app
    
    manager = Manager(app) # 接受用一个app
    
    if __name__ == '__main__':
        manager.run()
    

    添加脚本

    @manager.option("-u", "--name", dest="name")
    def add_user(name):
        print("hello %s" % name)
    

    添加一个函数,接受一个个参数,并且添加了一个个装饰器,接着我们通过命令执行文件,其中dest是接受传递的值:

    python manager.py add_user -u "ydy"
    或者
    python manager.py add_user --name "ydy"
    

    使用子命令

    我们可以新建一个db_script.py来管理我们操作数据库的命令文件

    from flask_script import Manager
    
    db_manager = Manager()
    
    @db_manager.command
    def init():
        pass
    
    @db_manager.command
    def makemigrations():
        pass
    
    @db_manager.command
    def migrate():
        pass
    

    之后我们在script主文件注册子命令:

    from flask_script import Manager
    from xxx import app # 从其他文件导入app
    from db_script import db_manager
    
    manager = Manager(app) # 接受用一个app
    manager.add_command("db",db_manager) # 添加子命令,名称为db,随意取
    
    if __name__ == '__main__':
        manager.run()
    

    使用子命令:

    python manager.py db init
    python manager.py db makemigrations
    python manager.py db migrate
    

    使用类定义

    举一个非常简单的例子,我们想创建一个hello仅打印出“ hello world” 的命令。它不需要任何参数,因此非常简单:

    from flask_script import Command
    
    class Hello(Command):
        "prints hello world"
    
        def run(self):
            print "hello world"
    
    manager.add_command('hello', Hello())
    

    使用类定义:

    • 必须继承Command
    • 必须实现run方法
    • 必须通过add_command方法添加命令

    通过类定义接受参数的形式:

    from flask_script import Command,Option
    
    class Hello(Command):
        option_list = (
            Option("-u", "--name", dest="name"),
        )
    
        def run(self, name):
            print("hello {}".format(name))
    
    manager.add_command("hello", Hello())
    
    #========================方式二=======================
    class Hello2(Command):
        def __init__(self):
            # 一般用来设置默认值
            pass
    
        def get_options(self):
            return [
                Option("-u", "--name", dest="name"),
            ]
    
        def run(self, name):
            print("hello {}".format(name))
    
    manager.add_command("hello2", Hello2())
    

    Flask-Migrate

    安装

    pip install flask-migrate -i https://pypi.doubanio.com/simple
    

    在manage.py代码中使用

    from flask_script import Manager
    from flask_migrate import Migrate,MigrateCommand
    from xxx import app
    from xxx import models # 需要导入模型,产生映射关系,否则无法生成迁移文件
    
    manager = Manager(app)
    
    # 用来绑定app和db到flask_migrate的
    Migrate(app,db)
    
    # 添加Migrate的所有子命令到db下
    manager.add_command("db",MigrateCommand)
    
    if __name__ == '__main__':
        manager.run()
    

    flask_migrate常用命令

    1. 初始化一个环境:python manage.py db init
    2. 自动检测模型,生成迁移脚本:python manage.py db migrate
    3. 将迁移脚本映射到数据库中:python manage.py db upgrade
    4. 更多命令:python manage.py db --help

    Flask-RESTful

    Flask-RESTful适用于前后端分离项目的接口设计,设计符合RESTful风格的代码,使用简单方便。Flask-RESTful要求Python版本为 2.6, 2.7, 或者 3.3及以上。

    安装

    pip install flask-restful -i https://pypi.doubanio.com/simple
    

    定义使用

    下面结合Flask-RESTful设计一个接口,新建一个manage.py的文件:

    from flask import Flask
    from flask_restful import Api, Resource
    
    app = Flask(__name__)
    
    api = Api(app=app)
    
    class Index(Resource):
        def get(self):
            return {'username': "ydongy"}
    
    api.add_resource(Index, "/", endpoint="index")
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    分析:

    1. 导入模块from flask_restful import Api, ResourceApi接受一个app生成一个对象,相当于把app包装。Resource是需要继承的类,替换以前的views.MethodView
    2. api = Api(app=app),把app包装
    3. class Index(Resource)::定义的类视图继承Resource,此时返回的响应内容就是Json格式
    4. api.add_resource(Index, "/", endpoint="index"):添加路由和端点,endpoint用于url_for反转url使用,如果不写endpoint,那么将会使用视图的名字的小写来作为endpoint。

    参数解析

    Flask-RESTful插件提供了类似WTForms来验证提交的数据是否合法的包,叫做reqparse。以下是基本用法:

    from flask_restful import reqparse
    
    class Login(Resource):
        def post(self):
            parser = reqparse.RequestParser()
            parser.add_argument('username', type=str, help="用户名", required=True)
            parser.add_argument('password', type=str, help="密码", required=True)
            args = parser.parse_args()
            
            ......
    

    add_argument可以指定这个字段的名字,这个字段的数据类型等,也就是接受前台传递过来的请求体数据。以下将对这个方法的一些参数做详细讲解:

    常用参数:

    • default:默认值,如果这个参数没有值,那么将使用这个参数指定的值。
    • required:是否必须。默认为False,如果设置为True,那么这个参数就必须提交上来。
    • type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。
    • choices:选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。
    • help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。
    • trim:是否要去掉前后的空格。

    扩展type

    其中的type,可以使用python自带的一些数据类型,也可以使用flask_restful.inputs下的一些特定的数据类型来强制转换。比如一些常用的:

    • url:会判断这个参数的值是否是一个url,如果不是,那么就会抛出异常。
    from flask_restful import inputs
    
    parser.add_argument('urlpath', type=inputs.url, help="url地址", required=True)
    
    • regex:正则表达式。
    from flask_restful import inputs
    
    parser.add_argument('phone', type=inputs.regex(r'1[3-9]d{9}'), help="手机号")
    
    • date:将这个字符串转换为datetime.date数据类型。如果转换不成功,则会抛出一个异常。
    from flask_restful import inputs
    
    parser.add_argument('birthday', type=inputs.date, help="生日", required=True)
    

    数据格式化

    对于一个视图函数,你可以指定好一些字段用于返回。以后可以使用ORM模型或者自定义的模型的时候,他会自动的获取模型中的相应的字段,生成json数据,然后再返回给客户端。这其中需要导入flask_restful.marshal_with装饰器。并且需要写一个字典,来指示需要返回的字段,以及该字段的数据类型。示例代码如下:

    from flask_restful import marshal_with,fields
    
    class ProfileView(Resource):
        resource_fields = {
            'username': fields.String,
            'age': fields.Integer,
            'gender': fields.Integer
        }
    
        @marshal_with(resource_fields)
        def get(self,user_id):
            user = User.query.get(user_id)
            return user
    

    分析:

    1. resource_fields:定义一个需要返回的json格式的数据变量,名称随便取
    2. @marshal_with(resource_fields):给方法增加一个装饰器,并把定义的变量传入

    在get方法中,返回user的时候,flask_restful会自动的读取user模型上的username以及age还有gender属性。组装成一个json格式的字符串返回给客户端,因此不需要我们手动调用各个字段,组织成json数据返回。但是注意定义的字典中的键需要和模型字段名一致,如果想要不一样可以增加一个参数,如下重命名属性。

    重命名属性

    有时候返回给前台的的字段名称的不同于模型的属性名。此时可以使用attribute配置这种映射。比如现在想要返回user.username中的值,但是在返回给前台的时候,想要以name返回回去,那么可以这样写:

    resource_fields = {
        'name': fields.String(attribute='username')
    }
    

    attribute:对应模型字段属性名称,name是返回给前端的字段名称。
    还可以定义一个default默认值,当字段没有值的时候可以指定一个默认值。

    关联关系

    首先我们定义几个模型类,表示之间的关联关系:

    class User(db.Model):
        __tablename__ = "tb_user"
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(50))
        email = db.Column(db.String(200))
    
    
    article_tag_table = db.Table(
        'tb_tag_article',
        db.Column('article_id', db.Integer, db.ForeignKey("tb_article.id"), primary_key=True),
        db.Column('tag_id', db.Integer, db.ForeignKey("tb_tag.id"), primary_key=True)
    )
    
    
    class Article(db.Model):
        __tablename__ = "tb_article"
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(50))
        content = db.Column(db.String(200), nullable=True)
        author_id = db.Column(db.Integer, db.ForeignKey('tb_user.id'))  # 表名.id
    
        author = db.relationship("User", backref="articles")
    
        tags = db.relationship("Tag", backref="articles", secondary=article_tag_table)
    
    
    class Tag(db.Model):
        __tablename__ = "tb_tag"
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(50))
    
    • user-article:一对多的关系
    • article-tag:多对多的关系

    此时我们想要查询一个文章,但是查询结果中还要包含该文章的作者、所属标签的信息,而不是单单的外键id。此时需要如下定义,一对多的关系可以使用fields.Nested,多对多的关系可以使用fields.List

    class ArticleView(Resource):
        resource_fields = {
            'title': fields.String,
            'content': fields.String,
            'author': fields.Nested({
                "id": fields.Integer,
                "username": fields.String,
                "email": fields.String
            }),
            'tags': fields.List(fields.Nested({
                "id": fields.Integer,
                "name": fields.String
            }))
        }
    
        @marshal_with(resource_fields)
        def get(self, article_id):
            article = Article.query.get(article_id)
            return article
    
    • author:就是Article关联字段的属性,表示单个,所以使用fields.Nested,参数是想要获取的关联对象的属性
    • tags:也是Article关联字段的属性,表示多个,所以使用fields.List,而每一个tag又是一个关联对象,所以嵌套一层fields.Nested,参数是想要获取的关联对象的属性

    返回结果如下:

    {
        "title": "mysql",
        "content": "入门到删库",
        "author": {
            "id": 1,
            "username": "jack",
            "email": "123@163.com"
        },
        "tags": [
            {
                "id": 1,
                "name": "database"
            },
            {
                "id": 2,
                "name": "language"
            }
        ]
    }
    

    蓝图及模板使用

    • 在蓝图中,如果使用flask-restful,那么在创建Api对象的时候,就不要再使用app了,而是使用蓝图app。
    • 如果在flask-restful的视图中想要返回html代码,或者是模版,那么就应该使用api.representation这个装饰器来定义一个函数,在这个函数中,应该对html代码进行一个封装,再返回。
    from flask import render_template, make_response
    from flask import Blueprint
    from flask_restful import Api
    
    api_bp = Blueprint(name="v1", import_name=__name__, url_prefix="/v1")
    api = Api(app=api_bp)
    
    @api.representation('text/html')
    def html(data, code, headers):
        resp = make_response(data)
        return resp
    
    
    class ListView(Resource):
        def get(self):
            return render_template("index.html")
    
    api.add_resource(ListView, '/article/', endpoint="list_view")
    

    分析:

    1. api_bp = Blueprint(name="v1", import_name=__name__, url_prefix="/v1"):定义蓝图
    2. api = Api(app=api_bp):传入蓝图app创建Api对象
    3. @api.representation('text/html'):装饰一个函数,指明返回的content-type类型,该方法自动执行,所以需要返回一个response对象
    4. return render_template("index.html"):restful类视图返回模板,会自动执行上面被装饰器定义过的函数
  • 相关阅读:
    php上传进度条
    array_combine — 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值
    password_hash 与 password_verify
    这是一个微信带参数的二维码,自定义菜单,与图文回复
    go to 语句用起来还是挺方便的
    初次使用海豚php的一个例子
    图片下载
    一对一的关联映射
    延迟加载
    proxy和proxy-no的策略取值区别
  • 原文地址:https://www.cnblogs.com/ydongy/p/13158310.html
Copyright © 2020-2023  润新知