• Flask基础知识


    开学一个月时间,课余时间,半摸鱼半学习,大概过了下Flask的基础知识.
    这是我的学习笔记,在此做一个记录.

    第一个Flask程序

    from flask import Flask
    app = Flask(__name__)   //初始化Flask对象(__name__用于确定程序根目录)
    @app.route('/')     //路由
    def hello_world():  //视图函数
        return "Hello World";
    
    if __name__=='__main__':
        app.run()   //run可以传入debug,port,host等参数
    /*主函数运行程序,导入模块不执行*/
    

    URL传递参数

    @app.route('/sayhi/<name>')
    def sayhi(name):
        return "<h1>Hi!%s</h1>"%name;
    
    • 在路由地址中,使用<>包含参数
    • 在视图函数中,传入该参数,以供调用

    URL反转和重定向

    @app.route('home')
    def home():
        return redirect(url_for(endpoint="sayhi",name="Jack"))
    
    • 通常我们通过路由跳转到指定的视图函数,但是有时候需要重定向到指定网页时,通过传入视图函数,获得对应路由地址,这个过程就叫做URL反转(需要用到Flask的url_for函数)
    • 重定向的话则需要用到flask的redirect函数

    Jinjia2 模板引擎

    • Flask使用Jinjia2模板引擎来实现复杂页面的渲染
    • 渲染:使用真实值替代网页模板中的变量,生成对应数据的HTML片段
    • Flask通过render_template()函数实现模板的渲染
    @app.route('/')
    def index():
        return render_template('index.html',name="Jack");   //传入参数渲染模板
    
    <!-- index.html -->
    <html>
    <head>
        <title>Home</title>
    </head>
    <body>
    <h1>
        {% if name %}   
        Hi!{{ name }}
        {% else %}
        Please Input Your Name
        {% endif %}
    </h1>
    </body>
    </html>
    
    • 通过{{ }}在模板内接受参数
    • 可以通过在视图函数中使用**locals()函数,向模板传入所有变量值
    • 也可以在模板中使用if判断语句,for循环,过滤器等
    • 可以通过宏(Marco)实现模板内控件的简化和复用
      • 宏的导入类似于import
    • include可以将一个模板导入到另一个模板(index.html导入navbar.html,header.html...)
    • 静态文件(css,js,images)加载,可在模板中通过url_for()与标签相结合进行调用
    • 模板中使用set定义变量
    • 模板继承 {{% extends "TemplateName" %}}
    • 模板块{{% block BlockName %}}{{% endblock %}}实现模板块的复用

    app.route和add_url_rule

    def hello_world():
        return "HelloWorld!";
    app.add_url_rule('/helloworld/',endpoint='helloworld',view_func=hello_world);
    
    • 在app.route()中或者app.add_url_rule()中指定了endpoint的话,之后便无法通过传递ViewFunction给url_for获得对应URL,而是要通过设定的endpoint
    • app.route修饰器会将URL和视图函数的关系保存到app.url_map
    • app.add_url_for()还可以设置指定method

    Flask类视图

    标准视图函数

    class Index(views.View):
        def dispatch_request(self):
            return render_template('home.html');
    
    app.add_url_rule(rule='/',endpoint='index',view_func=Index.as_view('index'));
    
    • 必须继承flask.view.View
    • 必须实现dispatch_request方法,相当于之前的视图函数
    • 类视图支持继承,但必须通过app.add_url_rule()注册

    基于方法的类视图

    class LoginView(views.MethodView):
        def get(self):
            return render_template('index.html');
        def post(self):
            username=request.form.get("username");
            password=request.form.get("pwd");
            if(username=='admin' and password=='admin'):
                return "Welcome Admin!";
            else:
                return "Username or Password is wrong,try again.";
    app.add_url_rule('/login',view_func=LoginView.as_view('loginview'));
    
    <!-- index.html -->
    <html>
    <body>
    <form action="login" method="post">
        <input type="text" class="input" name="username" placeholder="Please Input Username">
        <input type="password" class="input" name="pwd" placeholder="Please Input Password">
        <input type="submit" class="input button" value="Login">
    </form>
    </body>
    </html>
    
    • 继承flask.views.MethodView
    • 对每个HTTP Method执行不同函数

    Flask装饰器

    通过装饰器实现代码复用.

    Blueprint

    • Flask蓝图体哦那个了模块化管理程序路由的功能.
      • 让各个模块的视图函数写在不同的py文件中
      • 在主视图中导入分路由视图额模块,并注册蓝图对象
    • 分路由中建立Blueprint对象代替Flask App对象
    • 主路由中导入各分路由模块,并通过app.register_blueprint()方法注册各分路由blueprint对象到app
    import blueprint_test
    
    app = Flask(__name__)
    
    app.register_blueprint(blueprint_test.testlist)
    
    
    #blueprint_test.py
    from flask import Flask,Blueprint
    
    testlist=Blueprint('test',__name__)
    @testlist.route('/test')
    def test():
        return "This is a Blueprint Test.";
    
    

    Flask-WTF 表单

    from flask_bootstrap import Bootstrap
    import config
    from form import BaseLogin
    app = Flask(__name__)
    app.config.from_object(config)
    Bootstrap(app)
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        form = BaseLogin()
        if form.validate_on_submit():
    
            return "表单提交成功"
        else:
            return render_template('BootstrapTemplate.html', form=form)
    
    
    #form.py
    from flask_wtf import FlaskForm
    from wtforms import StringField,PasswordField,SubmitField
    from wtforms.validators import DataRequired,Length
    class BaseLogin(FlaskForm):
        name=StringField('name',validators=[DataRequired(message="用户名不能为空"),Length(6,16,message="长度位于6~16之间")],render_kw={'placeholder':"输入用户名"})
        password=PasswordField('password',validators=[DataRequired(message="用户名不能为空"),Length(6,16,message="长度位于6~16之间")],render_kw={'placeholder':"输入密码"})
        submit=SubmitField("Submit")
    
    
    <!-- BootstrapTemplate.html -->
    {% extends"bootstrap/base.html" %}
    {% block title %}This is an example page{% endblock %}
    
    {% block navbar %}
    <div class="navbar navbar-fixed-top">
      <!-- ... -->
    </div>
    {% endblock %}
    
    {% block content %}
      <h1>Hello, Bootstrap</h1>
    {% import 'bootstrap/wtf.html' as wtf %}
    {{ wtf.quick_form(form) }}
    {% endblock %}
    
    
    • Bootstrap-WTF导入wtf.html(包含wtf宏)后,可以快速渲染WTF表单
    • WTF可以通过各类Field和Validator快速制作表单,记得新建表单要继承FlaskForm
    • 表单相关路由要指定方法,并指定表单对象,并可通过form.validate_on_submit()判断渲染提交后效果
    • 必须先设定app.config.from_object(config),SECRET_KEY,防止CSRF后才可以向渲染函数传入表单

    Flask-WTF CSRF保护

    #config.py
    CSRF_ENABLED = True
    SECRET_KEY = 'helloworld'
    
    
    #main.py
    from flask_wtf.csrf impot CSRFProtect
    import config
    app = Flask(__name__)
    app.config.from_object(config)
    CSRFProtect(app)
    
    
    {{ form.csrf_token }}
    <!-- 或者通过导入bootstrap,wtf.quick_form(form)会自动调用宏渲染 -->
    
    

    Flask-WTF 上传文件

    from flask_wtf.file import FileField, FileRequired
    from wtforms import SubmitField
    from werkzeug.utils import secure_filename
    import os
    
    class UploadForm(FlaskForm):
        textFile = FileField(validators=[FileRequired()])
        submit = SubmitField('Submit')
    
    @app.route('/upload', methods=['GET', 'POST'],endpoint='upload')
    def upload():
        form = UploadForm()
        if form.validate_on_submit():
            f = form.textFile.data
            filename = secure_filename(f.filename)
            f.save(os.path.join(app.instance_path, 'text', filename))
            return redirect(url_for('upload'))
        return render_template('Upload.html', form=form)
    
    
    <!-- Upload.html -->
    {% extends "bootstrap/base.html" %}
    {% import 'bootstrap/wtf.html' as wtf %}
    {% block body %}
    {{ wtf.quick_form(form) }}
    {% endblock %}
    
    
    • 可以使用filename.rsplit('.',1)来获取文件后缀
    • 可以调用flask_wtf.file.FileRequired验证文件上传是否为空
    • 调用flask_wtf.file.FileAllowed指定上传文件类型

    Python文件路径

    • os.path.join(path1[, path2[, ...]]) //把各个目录,文件名连接起来,返回一个完整路径
    • app.route_path //获取Flask的根目录
    • Runoob-Python路径

    Flask Cookies

    @app.route('/<name>')
    def SetCookie(name):
        response = make_response(render_template('home.html'))
        response.set_cookie("name", name)
        return redirect(url_for('CookieTest',name=name))
    
    @app.route('/')
    def CookieTest():
        name = request.cookies.get('name')
        if name:
            response = make_response(render_template('home.html', name=name))
            return response
        print(url_for('SetCookie',name=""))
        return redirect()
    
    
    <!-- home.html  -->
    <html>
    <head>
        <title>Home</title>
    </head>
    <body>
    <h1>
        {% if name %}
        Hi!{{name|replace('J','H')}}
        {% else %}
        Please Input Your Name
        {% endif %}
    </h1>
    <h2>
        {% block content %}
        {% endblock %}
    </h2>
    </body>
    </html>
    
    
    • render_template返回的是Unicode字符串,想要添加Cookie需要先经过make_response(),转换成Response对象后再进行添加
    • 查看Cookie值使用request.cookies.get()方法

    Flask Session

    • 与之前的Flask Cookies差距不大,同样是设置,获取,清除几个操作
    • 不同之处在于,Cookie存在客户端本地,Session存在服务器,不做设置的话,Session在浏览器关闭后便会自动清除

    钩子函数

    每次正常执行代码之前或者之后都会执行的函数,一种特定的修饰器函数.

    • before_first_request()
    • before_request()
    • after_request()
    • teardown_request()

    SQLAlchemy

    import os
    basedir = os.path.abspath(os.path.dirname(__file__))
    class Config(object):
        SQLALCHEMY_DATABASE_URI = 'sqlite:///'+os.path.join(basedir,'app.db')
        SQLALCHEMY_TRACK_MODIFICATIONS= False
    
    
    #main.py
    from flask_sqlalchemy import SQLAlchemy
    app = Flask(__name__)
    db = SQLAlchemy(app)
    app.config.from_object(config.Config)
    
    
    #models.py
    from main import db
    
    class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(80), unique=True, nullable=False)
        email = db.Column(db.String(120), unique=True, nullable=False)
    
    
    #Python Console
    from models import db
    db.create_all()	#创建并初始化sqlite数据库
    
    from models import User
    user = User(id=1,username="Jack",email="jack@gmail.com")	#建立User实例
    db.session.add(user)	#向db添加user实例
    db.session.commit()		#向db提交user实例
    User.query.all()[0].username	#查找所有User实例,中第一项的username参数
    
    User.query.delete()		#删除所有User实例
    db.session.commit()		#确认删除所有User实例
    
    
    • SQLAlchemy是Python下的一个SQL ORM(Object Relational Mapping)工具
      • 将SQL面向过程的概念映射到Python面向对象的概念上去
        • 类=>表
        • 对象=>表的行(记录)
        • 属性=>表的列(字段)
      • 使用CRUD的API,代替冗长的SQL语句
    • 在初始化时一样是使用app.config.from_object()导入config.py文件当中的配置
      • config.py当中需要的配置是
        • SQLALCHEMY_DATABASE_URI
          • ('数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名')
            • 'mysql+mysqlconnector://root:password@localhost:3306/test'
            • 'sqlite:///'+os.path.join(basedir,'app.db')
        • SQLALCHE_TRACK_MODIFICATIONS
          • 动态追踪修改设置,未设置会提示未设置
    • 建立类时
      • 继承db.Model(db=SQLlchemy(app))
      • 字段为db.Column
      • 最后使用db.create_all()方法建表
    • 添加记录时,先调用db.session.add()将对象添加到session,之后再调用commit方法提交
    • 数据查询
      • ClassName/TableName.query.fliter(ClassName.FieldName=='Value').first/all()
        • 返回的是查询结果,经过滤后的一个或者所有对象
          • 之后可以再访问它们的具体字段
          • 可以不经过滤返回所有对象
    • 数据修改
      • 直接对返回对象进行赋值,然后commit修改即可完成对数据的修改
    • 数据删除
      • db.session.delete(obj)删除对象后commit即可完成删除
    • 多对多关系
      • 通过relationship("TableName",backref="__tablename")和Column(,ForeignKey('TABLE.COLUMN'))的组合可以建立一个相互的映射,从而实现两个表之间的相互调用

    循环引用

    项目变大可能会遇到循环引用的问题,这个时候就要注意架构是否合理.

    MVC分离,按照flask官方推荐,forms,models,errors几个模块建立在utils之上,views是最顶层,上级模块可以调用下级模块,下级模块不能调用上级模块,这样合理架构,就可以避免循环引用.(简言之,按照模型规范来,不要没想要,瞎调用)

    Flask RESTful API

    Requirements

    • Flask(Flask,jsonify)
    • Flask-SQLAlchemy
    • Flask-Marshmallow=>Serialization/Deserialization library

    How to use Marshmallow

    # Create your app
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_marshmallow import Marshmallow
    
    app = Flask(__name__)
    db = SQLAlchemy(app)
    ma = Marshmallow(app)
    
    # Write your models
    class User(db.Model):
        id=db.Column(db.Integer,primary_key=True)
        name=db.Column(db.String(100),unique=True)
        email=db.Column(db.String(100),unique=True)
    
    # Define your output format with marshmallow
    class UserSchema(ma.Schema):
        class Meta:
            #Fields to expose
            fields = ("name","email")
    user_schema = UserSchema()
    users_schema = UserSchema(many=True)
    
    # Output the data in your views
    @app.route("/api/user/<id>")
    def user_detail(id):
        user = User.get(id)
        return user_schema.dump(user)
    
    

    How to write RESTful CRUD API

    from flask import Flask,jsonify
    from flask_sqlalchemy import SQLAlchemy
    from flask_marshmallow import Marshmallow
    # Init app
    app = Flask(__name__)
    basedir = os.path.abspath(os.path.dirname(__file__))
    
    # Database
    app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///'+os.path.join(basedir,'db.sqlite')
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
    # Init DB
    
    # Init ma
    ma = Marshmallow(app)
    
    # Product Class/Model =>Flask-SQLAlchemy Document
    class Product(db.Model):
        id=db.Column(db.Integer,primary_key=True)
        name = db.Column(db.String(100),unique=True)
        description = db.Column(db.String(200))
        price = db.Column(db.Float)
        qty = db.Column(db.Integer)
        
        def __init__(self,name,description,price,qty):
            self.name=name;
            self.description=description
            self.price=price
            self.qty=qty
    
    # Product Schema
    class ProductSchema(ma.Schema):
        class Meta:
            fields = ('id','name','description','price','qty')
            
    # Init schema
    product_schema = ProductSchema(strict=True)
    products_schema = ProductSchema(many=True,strict=True)
    
    # Create a Product
    @app.route('/product',methods=['POST'])
    def app_product():
        name =request.json['name']
        description =request.json['description']
        price =request.json['price']
        qty =request.json['qty']
        
        new_product = Product(name,description,price,qty)
        db.session.add(new_product)
        db.session.commit()
        
        return product_schema.jsonify(new_product)
    
    # Get All Products
    @app.route('/product',methods=['GET'])
    def get_products():
        app_products = Product.query.all()
        result = products_schema.dump(all_products)
        return jsonify(result.data)
    
    # Get Single Products
    @app.route('/product/<id>',methods=['GET'])
    def get_product(id):
        product = Product.query.get(id)
        return product_schema.jsonify()
    
    # Update a Product
    @app.route('/product/<id>',methods=['PUT'])
    def update_product(id):
        product = Product.query.get(id)
        
        name =request.json['name']
        description =request.json['description']
        price =request.json['price']
        qty =request.json['qty']
        
        product.name = name;
        product.description = description;
        product.price = price;
        product.qty = qty;
        
        db.session.commit()
        
        db.session.add(new_product)
        db.session.commit()
        
        return product_schema.jsonify(new_product)
    
    # Delete Product
    @app.route('/product/<id>',methods=['DELETE'])
    def delete_product(id):
        product = Product.query.get(id)
        db.session.delete(product)
        db.session.commit()
        return product_scheema.jsonify()
    
    # Run Server
    if __name__ == '__main__':
        app.run(debug=True)
    

    MVC架构的RESTful API

    前面这些大都是单文件的程序,但具体工程中肯定是需要对各个功能模块进行分离的,否则整个项目将变得难以维护.

    所以在看了一些别人的项目,也学习了解了MVC的基础知识后,我重新写了个功能模块划分得比较清楚的demo

    Flask-REST-DEMO

    因为就是一个比较小的TODO,对Controller进行拆分,分为Controller和Service显得有些多余,但是这样的结构应该是合理的.在比较大的项目就会发挥它的优势了.

    对后端的具体实现大概了解了,学习暂且告一段落.

    之前了解过Flutter,现在想要了解下Vue,然后再决定以后主攻方向.

    可能会再回来学习各个中间件的使用和优化,或者跑去学Go.

    就先这样啦.

  • 相关阅读:
    virtualbox 安装windows系统的一些问题
    JavaScript字符串替换replace方法
    使用递归制作仿京东淘宝的商品分类导航栏
    Ubuntu 安装
    easyui 常用代码
    HTML的Get方法URL传递中文参数,解决乱码问题
    PhoneGap 在eclipse上开发Android程序
    C# ashx与html的联合使用
    mysql 操作指令笔记
    mysql 安装employees db的步骤
  • 原文地址:https://www.cnblogs.com/rpish/p/13802992.html
Copyright © 2020-2023  润新知