Flask基础知识

摘要:
“;app.add_url_rule;如果在app.route()或app.add_url_rule()中指定了端点,则无法通过将ViewFunction传递给url_fr来获得相应的url。相反,url和视图函数之间的关系将保存到app.url_mapapp.add_url_for()中通过set-endpointapp。route decorator您还可以设置指定的方法Flask类视图标准视图函数classIndex:defdispatch_request:returnrender_template;app.add _ url_规则;必须继承flask.view视图必须实现dispatch_请求方法,它等效于以前的视图函数类视图,支持继承,但必须通过应用程序add_ url_Rule()注册基于方法的类视图类LoginView:define:returnrender_ template;defpost:username=request.form.get;密码=request.form.get;如果:return“WelcomeAdmin!

开学一个月时间,课余时间,半摸鱼半学习,大概过了下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"   name="username" placeholder="Please Input Username">
    <input type="password"   name="pwd" placeholder="Please Input Password">
    <input type="submit"   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.

就先这样啦.

免责声明:文章转载自《Flask基础知识》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇itextPDF使用笔记3Dslicer_AnnotationsModule下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

(四)下载利器aria2

既然较私有云,那么离线下载这最起码的功能应该得要支持吧,这时候发现了aria2。 这玩意简直是大杀器,支持http也支持bt下载,性能不弱,速度也不逊色于迅雷,缺点就是没界面。 没界面没关系,还有万能的基友们,带上AriaNg就万事俱备了。 首先,安装aira2,老配方: 偷懒的同学可以直接选用前4个镜像,我喜欢单独装,因为这样就可以自己diy了,所以选...

往android的内核添加驱动及 ueventd.rc 修改【转】

本文转载自:http://blog.csdn.net/lkqboy2599/article/details/8350100 向android的内核添加驱动,其实就是向linux内核添加驱动。主要在两个文件里添加点信息,一个是Kconfig文件,一个是Makefile文件。比如你添加的驱动你代码放到drivers目录下面的XXX目录,那么修在该目录下的Kco...

logstash5安装并实现mariadb数据写入到elasticsearch

java环境这里默认安装了 ,一般源码安装,这里就不说了 一、安装logstash 安装logstash可以用yum安装,也可以用源码安装: yum安装: 1.导入GPG: rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch 2.创建repo文件,vim /etc/yum.repo...

Webpack安装配置及打包详细过程

引言 前端经过漫长的发展,涌现出了很多实践方法来处理复杂的工作流程,让开发变得更加简便,其中,模块化可以使复杂的程序细化成为各个小的文件,而webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目,本文详细介绍webpack安装配置及打包的详细过程。 Webpack简单介绍 本质上,webpack是一个现代 JavaScrip...

babel.config.js 和 .babelrc 对比

Babel 有两种并行的配置文件格式,可以一起使用,也可以分开使用。 项目范围的配置 babel.config.js 文件,具有不同的拓展名(json、js、html)babel.config.js 是按照 commonjs 导出对象,可以写js的逻辑。 相对文件的配置 .babelrc 文件,具有不同的拓展名 总结:baberc 的加载规则是按目录加载的...

【H5】15 表单 其四 数据发送

一旦在客户端上验证了表单数据,就可以提交表单了。 并且,由于我们在上一篇文章中介绍了验证,因此我们准备提交! 本文着眼于用户提交表单时会发生什么—数据将流向何处,以及到达表单后如何处理? 我们还将研究与发送表单数据相关的一些安全问题。 先决条件: 基本的计算机知识,对HTML的理解以及HTTP和服务器端编程的基本知识。 目的: 了解提交表单数据...