Flask快速学习开发指南

前言:

之前学过flask,不过也只是为了做题,看得懂但是写不来,最近突然要写个轻型项目,真正上手的时候发现flask是真的轻型方便,但是发现很多操作已经忘的差不多了,这篇文章除了博客的作用还有笔记的作用,用来提醒自己和帮助一些人通过flask快速上手开发小型项目,本文不会介绍很多少见的高级操作,大部分都是很常见很简单很常用的操作,目的还是一个快速上手

这篇文章需要一些基础,相当于复习巩固用的

flask

创建项目

Pycharm可以跳过了,这里介绍一下linux下如何用virtualenv

pip3 install virtualenv
virtualenv -p /usr/bin/python3 --no-site-packages venv
#-p后面跟的是py绝对路径,不加默认使用系统py版本
#--no-site-packages表示不导入系统已有的第三方包
source venv/bin/activate
pip install flask


#退出虚拟环境
deactivate

最简单的项目结构

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


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

蓝图

文字解释

蓝图其实不难理解,flask通过@app.route实现路由功能,当一个项目变大了,我们肯定希望功能相同的路由封装在相同的模块中
比如

  • /admin/login
  • /admin/index
  • /admin/registry

  • /space/index
  • /space/login

但是默认方法只能把所有路由都写在一个文件中(如果尝试从其他文件中导入路由会报错),通过@app.route一个一个硬写在同一个文件,这样肯定不方便维护和代码美观,为了把不同的功能分模块分开,就有了蓝图这个概念,一个蓝图代表了一个模块和一个路由前缀

代码解释

1
__init__.py内容为空,代表admin文件夹为一个模块

#views.py
from flask import Blueprint

admin_blueprint = Blueprint("admin", __name__)
#Blueprint的第一个参数是蓝本的名字
#第二个参数是篮本所在的包或模块,一般用__name__

@admin_blueprint.route('/')     #注意不是@app.route
def index():
    return "U are in admin blueprint"
#app.py
from flask import Flask
from admin.views import admin_blueprint

app = Flask(__name__)
app.register_blueprint(admin_blueprint,url_prefix="/admin")

@app.route('/')
def hello_world():
    return 'Hello World!'


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

访问http://127.0.0.1:5000/admin/
15474918062058.jpg

这样就实现了功能的模块封装

flask-bootstrap

template/base.html

<!--base.html-->
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
         <div class="container">
             <div class="navbar-header">
                 <button type="button" class="navbar-toggle"
                  data-toggle="collapse" data-target=".navbar-collapse">
                     <span class="sr-only">Toggle navigation</span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
</button>
                 <a class="navbar-brand" href="/">Flasky</a>
             </div>
             <div class="navbar-collapse collapse">
                 <ul class="nav navbar-nav">
                     <li><a href="/">Home</a></li>
                 </ul>
             </div>
         </div>
</div>
{% endblock %}
{% block content %} <div class="container">
{% block page_content %}{% endblock %} </div>
{% endblock %}

其他html可以继承此模板,通过

{% extends "base.html" %}

{% block %}修饰的地方可以被子类重写
比如404.html

<!--404.html-->
{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
    <div class="page-header">
         <h1>Not Found</h1>
    </div>
{% endblock %}

然后记得app.py中

from flask_bootstrap import Bootstrap

Bootstrap(app)
#告诉bootstrap拓展这个项目的绝对路径

flask_wtf

把所有表单抽象成一个类

#Form.py
from flask_wtf import Form
from wtforms import StringField,SubmitField,PasswordField, ValidationError
from wtforms.validators import Required,DataRequired,Regexp

#继承Form
class LoginForm(Form):
    username = StringField(
        label="Please Input Username",
        validators=[DataRequired(),
        Regexp('^[A-Za-z][A-Za-z0-9_.]*$',0,"fuck")],
        render_kw={"placeholder":"Username"},
        description='用户名'
        #第一个参数默认是label显示位置在输入框上方
        #Regexp是后端验证,经过validate_on_submit函数验证,第二个参数默认0,第三个是错误信息
        #render_kw是自定义属性和值,可以自定义class,placeholder等,通过字典形式
        #description类似于label,但显示位置在输入框下方且字体较小
    )
    password = PasswordField("Please inout password",validators=[DataRequired()])
    submit = SubmitField("提交")
   
    def validate_username(self,field):
        if field.data != "evoA":
            raise ValidationError("must admin!")
            
/*
在表单类中定义validate_ + 表单字段名(如本例的usernamae,password,submit)开头的函数
为自定义验证函数,第一个参数为self,第二个参数为field代表字段自身,field.data代表字段的值
通过抛出ValidationError(错误提示信息)表示验证不通过,此函数会自动验证对应的字段而无需添加到
validators=的列表中
*/

超爽的flask_bootstrap的快速渲染

<!--template/index.html-->
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% endblock %}

app.py

#app.py
from flask import Flask
from flask_bootstrap import Bootstrap
from Form import LoginForm

app = Flask(__name__)
app.config['SECRET_KEY'] = 'guess?'     #flask_wtf必须设置SECRET_KEY
Bootstrap(app)

@app.route('/user', methods=['GET', 'POST'])
def login():
    form = LoginForm()  #必须传实例
    return render_template('index.html', form=form)
    
if __name__ == '__main__':
    app.run(debug=True)

flask_sqlalchemy

直接上代码吧

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///' + os.path.join(basedir, 'data.db')
# 数据库的地址,注意是URI不是URL 巨坑!
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
# 访问结束自动提交
db = SQLAlchemy(app)

class User(db.Model):
    __tablename__ = "users"
    # 这里定义的才是表名
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(50),nullable=False,unique=True,index=True)
    password = db.Column(db.String(50),nullable=False)
    # 定义了三个字段id,username,password

@app.route('/')
def hello_world():
    db.create_all()
    # 建立数据库文件
    user1 = User(username="evoA",password="123456")
    user2 = User(username="test",password="test")
    # 新建了两个用户实例
    db.session.add(user1)
    db.session.add(user2)
    # 提交事务,这时候数据库文件并未修改,
    db.session.commit()
    # 这时候数据库文件正式修改添加了两条新数据
    return 'Hello World!'

@app.route('/password')
def password():
    return str(User.query.filter_by(username="evoA").first().password)
    #查询username为evoA对应的password

@app.route('/delete')
def delete():
    db.session.delete(User.query.filter_by(username="test").first())
    #删除了test用户
    db.session.commit()
    return 'delete!'

@app.route("/change")
def change():
    evoA = User.query.filter_by(username="evoA").first()
    evoA.password = "6666666"
    #更改了evoA用户的密码
    db.session.add(evoA)
    db.session.commit()
    return "change!"
    
if __name__ == '__main__':
    app.run()

flask_login

文字理解

处理登录注销逻辑和用户管理逻辑的库,只需要简单配置,就能与数据库和表单交互处理用户登录逻辑

代码理解

from flask_login import UserMixin, LoginManager,login_user,login_required

login_manage = LoginManager()
#实例化类
login_manage.session_protection = 'strong'
#session安全强度最强
login_manage.login_view = "login"
#设置登录的路由,如果在蓝图中则为 "蓝图路由前缀.login" 用 . 分割
login_manage.init_app(app)
#绑定到flask应用

#必须定义一个回调函数,函数名参数自定,返回一个用户的唯一标识符
@login_manage.user_loader
def user_load(user_id):
    return User.query.get(int(user_id))
    

class User(UserMixin, db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), nullable=False, unique=True, index=True)
    password = db.Column(db.String(50), nullable=False)
    

@app.route('/login', methods=["GET", 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        login_user(user)
        #login_user函数,传入用户对象使一个用户对象登录
        return render_template('login.html', form=form)
    else:
        return render_template('login.html', form=form)

@app.route('/secret')
@login_required     #login_required修饰器代表此路由必须登录才能访问!否则自动跳转登录界面
def secret():
    return "U see my secret"

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

用户类再继承一个UserMixin类,若不继承必须自己实现下列方法

15475403755429.jpg
继承则默认实现了上诉方法

flask_login绑定app应用后,在模板中会自动注册current_user变量,若要在模板中判断用户是否登录可以用如下模板

{% if current_user.is_authenticated %}
    <h1>{{ current_user.username }}</h1>
{% else %}
    <h1>No User Login</h1>
{% endif %}
<!--在新版flask_login中使用current_user.is_authenticated判断用户是否登录,
而不是老版的current_user.is_authenticated(),有些文章书上可能会用后者,新版
已经不支持-->

current_user相当于login_user函数传入的 用户类实例,current_user.username即是用户实例的username属性,同理current_user应该还有password属性等

如果需要登出用户只需要再引入一个logout_user函数即可,如下所示,此函数不需要参数

from flask_login import logout_user, login_required

@app.route('/logout')
@login_required
    def logout():
        logout_user()
        return "logout!"

login_user函数还有第二个参数为bool型,默认为False,代表用户关闭浏览器会话自动过期,若为True,则会话会长期有效。可以配合form表单的记住我

flask-admin

初始化

第一步是为Flask应用初始化一个空的管理界面:

from flask import Flask
from flask_admin import Admin

app = Flask(__name__)

# set optional bootswatch theme
app.config['FLASK_ADMIN_SWATCH'] = 'cerulean'

admin = Admin(app, name='microblog', template_mode='bootstrap3')
# Add administrative views here

app.run()

添加模型视图

模型视图允许您添加一组专用管理页面,用于管理数据库中的任何模型。通过创建ModelView类的实例来执行此操作,您可以从Flask-Admin的内置ORM后端之一导入该实例。一个例子是SQLAlchemy后端,您可以按如下方式使用它:

from flask_admin.contrib.sqla import ModelView

# Flask and Flask-SQLAlchemy initialization here

admin = Admin(app, name='microblog', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

添加文件管理

要管理静态文件而不是数据库记录,Flask-Admin附带了FileAdmin插件。它使您能够上传,删除,重命名等。您可以通过向您的应用添加FileAdmin视图来使用它:

from flask_admin.contrib.fileadmin import FileAdmin

import os.path as op

# Flask setup here

admin = Admin(app, name='microblog', template_mode='bootstrap3')

path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))