博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
狗书(flask基础)
阅读量:4344 次
发布时间:2019-06-07

本文共 16088 字,大约阅读时间需要 53 分钟。

  为什么选择使用flask?

  和其他框架相比, Flask 之所以能脱颖而出,原因在于它让开发者做主,使其能对程序具有全面的创意控制。

  Flask 中,你可以自主选择程序的组件,如果找不到合适的,还可以自己开发。

  Flask 提供了一个强健的核心, 其中包含每个 Web 程序都需要的基本功能,而其他功能则交给行业系统中的众多第三方扩展。
  一句话概括就是flask不是一个高度定制化的web框架,你可以做到随心所欲,使用任何可能的扩展来完成你的项目。

  狗书的代码已上传GitHub:

  Flask 有两个主要依赖:路由、调试和 Web 服务器网关接口(Web Server Gateway InterfaceWSGI)子系统由 Werkzeughttp://werkzeug.pocoo.org/)提供;模板系统由 Jinja2http://jinja.pocoo.org/)提供。 Werkzeug Jinjia2 都是由 Flask 的核心开发者开发而成。

  Flask 并不原生支持数据库访问、 Web 表单验证和用户认证等高级功能。这些功能以及其他大多数 Web 程序中需要的核心服务都以扩展的形式实现, 然后再与核心包集成。 

  安装

pip install flask

  初始化

  所有 Flask 程序都必须创建一个程序实例Web 服务器使用一种名为 Web 服务器网关接口(Web Server Gateway InterfaceWSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。

from flask import Flaskapp = Flask(__name__)

  Flask接受一个字符串作为参数,这个参数决定程序的根目录,以便于能找到相对于程序根目录的资源文件的位置,通常这种情况下都使用  __name__作为Flask参数。

  也就是说,此时web框架接收的请求都会通过flask实例化的对象进行处理。

  这里的初始化方式是最简单的初始化方式,后面会使用到更为复杂的初始化方式。

  路由和视图函数

  程序实例需要知道对每个 URL 请求运行哪些代码,所以保存了一个 URL Python 函数的映射关系。处理 URL 和函数之间关系的程序称为路由

  在 Flask 程序中定义路由的最简便方式,是使用程序实例提供的 app.route 修饰器,把修饰的函数注册为路由。

@app.route('/')def index():    return '

Hello World!

'

  这个函数的返回值称为响应,是客户端接收到的内容。如果客户端是 Web 浏览器, 响应就是显示给用户查看的文档(一般就是html页面)。

  程序实例用 run 方法启动 Flask 集成的开发 Web 服务器:

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

  有一些选项参数可被 app.run() 函数接受用于设置 Web 服务器的操作模式。在开发过程中启用调试模式会带来一些便利, 比如说激活调试器重载程序。要想启用调试模式, 我们可以把 debug 参数设为 True

  第一个程序:

from flask import Flaskapp = Flask(__name__)@app.route('/')def index():    return '

Hello World!

'@app.route('/user/
')def user(name): return '

Hello, {}!

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

  为了避免大量可有可无的参数把视图函数弄得一团糟, Flask 使用上下文临时把某些对象变为全局可访问。有了上下文,就可以写出下面的视图函数:

from flask import request@app.route('/')def index():    user_agent = request.headers.get('User-Agent')    return '

Your browser is %s

' % user_agent

  这里我们把request当作全局变量使用,实际生产中每个线程都处理不同的请求,那么他们的request必然是不同的。Falsk 使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。

  request就是一种python中的ThreadLocal对象。

  只在线程中是全局变量。

#转载至csdn,http://blog.csdn.net/hyman_c/article/details/52548540import threadinglocalobj = threading.local()#flask中的request就是这样的概念def threadfunc(name):    localobj.name = name    print('localobj.name is %s' % name)if __name__ == '__main__':    t1 = threading.Thread(target=threadfunc, args=('Hyman',))    t2 = threading.Thread(target=threadfunc, args=('liuzhihui',))    t1.start()    t2.start()    t1.join()    t2.join()

  在多线程服务器中客户端每建立一个链接,服务器就创建一个线程,每个线程中就会有一个request来表示客户端的链接请求信息。

  flask上下文全局变量

  没激活程序上下文之前就调用 current_app.name 会导致错误,但推送完上下文之后就可以调用了。 注意,在程序实例上调用 app.app_context() 可获得一个程序上下文。ctx.push()是激活上下文的操作,类似的,如果我们想要回收上下文,用ctx.pop()。

from hello import appfrom flask import current_appapp_ctx = app.app_context()app_ctx.push()current_app.name'hello'
from flask import gg.name='manno'

  g作为flask程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据。

  request请求对象,封装了客户端发送的HTTP请求的内容。

  session用户会话,用来记住请求(比如前后一个GET请求和一个POST请求)之间的值,从数据格式上来说它是字典类型。它存在于连接到服务器的每个客户端中,属于私有存储,会保存在客户端的cookie中。

session['name']=form.name.data

  URL 映射是 URL 和视图函数之间的对应关系。Flask 使用 app.route 修饰器或者非修饰器形式的 pp.add_url_rule() 生成映射。

app.url_mapMap([
index>,
' (GET, HEAD, OPTIONS) -> static>,
' (GET, HEAD, OPTIONS) -> user>])

  / /user/<name> 路由在程序中使用 app.route 修饰器定义。 /static/<filename> 路由是Flask 添加的特殊路由,用于访问静态文件。

  请求钩子

  Flask 支持以下 4 种钩子。

  before_first_request:注册一个函数,在处理第一个请求之前运行。

  before_request:注册一个函数,在每次请求之前运行。
  after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
  teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。

  注:在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g。例如, before_request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视

图函数时,视图函数再使用 g.user 获取用户。

  flask中具有四种钩子被做成了修饰器,我们在后端可以进行调用做相关的操作.使用钩子函数时,我们需要借助flask的全局变量g.g作为中间变量,在钩子函数和视图函数中间传递数据.我们先引入全局变量g。

  第一步,引入全局变量g

from flask import g

  第二步, 然后注册一个视图函数,用来显示g中的数据

@app.route('/test')  def test():      return g.string

  这里我们使用request之前的钩子

@app.before_first_request  def bf_first_request():      g.string = 'before_first_request'

  此时运行此路由显示的是g中传递的string变量(当然也可以不返回进行其他操作)。

  响应

  flask的响应包括模板(本质是字符串)和状态码。

@app.route('/')def index():    return '

Bad Request

', 400

  如果不想返回由 1 个、 2 个或 3 个值组成的元组, Flask 视图函数还可以返回 Response 对象。 make_response() 函数可接受 1 个、 2 个或 3 个参数(和视图函数的返回值一样),并

返回一个 Response 对象。

from flask import make_response@app.route('/')def index():    response = make_response('

This document carries a cookie!

') response.set_cookie('answer', '42')#创建对象,给对象加cookie return response

  flask重定向

from flask import redirect@app.route('/')def index():    return redirect('http://www.example.com')

  abort生成响应404,abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器。

from flask import abort@app.route('/user/
')def get_user(id): user = load_user(id) if not user: abort(404) return '

Hello, %s

' % user.name

  使用Flask-Script支持命令行选项

  Flask 的开发 Web 服务器支持很多启动设置选项,但只能在脚本中作为参数传给 app.run()函数。这种方式并不十分方便,传递设置选项的理想方式是使用命令行参数。

  Flask-Script 是一个 Flask 扩展,为 Flask 程序添加了一个命令行解析器。 Flask-Script 自带了一组常用选项,而且还支持自定义命令。

pip install flask-script
from flask.ext.script import Managermanager = Manager(app)if __name__ == '__main__':manager.run()

  专为 Flask 开发的扩展都暴漏在 flask.ext 命名空间下。 Flask-Script 输出了一个名为Manager 的类,可从 flask.ext.script 中引入。

  这个扩展的初始化方法也适用于其他很多扩展: 把程序实例作为参数传给构造函数,初始化主类的实例。 创建的对象可以在各个扩展中使用。在这里,服务器由 manager.run()

动,启动后就能解析命令行了。

from flask import Flaskfrom flask_script import Managerapp = Flask(__name__)manager = Manager(app)@app.route('/')def index():    return '

Hello World!

'@app.route('/user/
')def user(name): return '

Hello, %s!

' % nameif __name__ == '__main__': manager.run()

   使用manager可以增加自定义命令

from flask_script import Managerapp = Flask(__name__)manager=Manager(app)@manager.commanddef print_str():    print('hello world')if __name__ == '__main__':    manager.run()

  设置cookie

@app.route('/set_cookie')  def set_cookie():      response=make_response('Hello World');      response.set_cookie('Name','Hyman')      return response

  我们还可以指定cookie的有效时长,下面的代码把有效时长设置成了30天.通常情况下,我们还可以在浏览器上设置cookie的有效时长,而且浏览器上配置的有效时长优先级要高于我们在代码中设置的。

outdate=datetime.datetime.today() + datetime.timedelta(days=30)  response.set_cookie('Name','Hyman',expires=outdate)

  获取cookie

@app.route('/get_cookie')  def get_cookie():      name=request.cookies.get('Name')      return name

My name is {
{request.cookies.get('Name')}}

{
#html模板中获取cookie#}

  删除cookie(三种方式)

  (1) 可以通过在浏览器中设置来清除cookie

  (2) 使用Response的set_cookie进行清除

@app.route('/del_cookie')def del_cookie():    response=make_response('delete cookie')    response.set_cookie('Name','',expires=0)    return response

  (3)使用Response的 delete_cookie方法

@app.route('/del_cookie2')  def del_cookie2():      response=make_response('delete cookie2')      response.delete_cookie('Name')      return response

  Jinja2模板引擎

  Jinja2模板引擎是flask默认的模板引擎。

   Flask 提供的 render_template 函数把 Jinja2 模板引擎集成到了程序中 。使用方式与django的render基本一致。

  Jinja2 能识别所有类型的变量, 甚至是一些复杂的类型,例如列表、字典和对象。

A value from a dictionary: {

{ mydict['key'] }}.

A value from a list: {

{ mylist[3] }}.

A value from a list, with a variable index: {

{ mylist[myintvar] }}.

A value from an object's method: {

{ myobj.somemethod() }}.

  jinja2很大程度上和django的模板语言很类似,包括过滤器的使用,判断,循环,模板继承等。

  来说点特别的,jinja2支持宏(类似于函数,使用也和函数很像)。

  定义及使用宏:

{% macro render_comment(comment) %}    
  • {
    { comment }}
  • {
    % endmacro %}
      {
      % for comment in comments %} {
      { render_comment(comment) }} {
      % endfor %}

      重复使用宏需要将其保存在单独的文件中,然后在需要使用的模板中导入

    {% import 'macros.html' as macros %}
      {
      % for comment in comments %} {
      { macros.render_comment(comment) }} {
      % endfor %}

      除了重复使用宏的方式还可以重复导入代码块。

    common.html

    我是重复的代码片

      导入这个代码块

    {% include 'common.html' %}{
    % include 'common.html' %}{
    % include 'common.html' %}{
    % include 'common.html' %}

    Hello World

      jinja2还支持与django模板一样的模板继承功能。

      要想在flask程序中集成 Bootstrap, 显然要对模板做所有必要的改动。不过,更简单的方法是使用一个名为 Flask-Bootstrap Flask 扩展(这也是我喜欢flask的原因之一吧,扩展性强,插件还很多),简化集成的过程。 

    pip install flask-bootstrap

      初始化 Flask-Bootstrap

    from flask.ext.bootstrap import Bootstrapbootstrap = Bootstrap(app)

      初始化 Flask-Bootstrap 之后,就可以在程序中使用一个包含所有 Bootstrap 文件的基模板。

    {% extends "bootstrap/base.html" %}{
    % block title %}Flasky{% endblock %}

    {% extends "bootstrap/base.html" %}{
    % block title %}Flasky{% endblock %}{
    % block navbar %}
    {
    % endblock %}{
    % block content %}
    {
    % endblock %}

      自定制错误页面

    @app.errorhandler(404)def page_not_found(e):    return render_template('404.html'), 404@app.errorhandler(500)def internal_server_error(e):    return render_template('500.html'), 500

       url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用的端点名)作为参数, 返回对应的 URL。例如,在当前版本的 hello.py 程序中调用 url_for('index') 得到的结果是 /。调用 url_for('index', _external=True) 返回的则是绝对地址,在这个示例中是 http://localhost:5000/

      使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如, url_for('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john
      如果 Web 程序的用户来自世界各地,那么处理日期和时间可不是一个简单的任务。
      有一个使用 JavaScript 开发的优秀客户端开源代码库,名为 moment.jshttp://momentjs.com/),它可以在浏览器中渲染日期和时间。 Flask-Moment 是一个 Flask 程序扩展,能把moment.js 集成到 Jinja2 模板中。

    pip install flask-moment

      此模块依赖于moment.js 和jquery.js

    from datetime import datetime@app.route('/')def index():    return render_template('index.html',current_time=datetime.utcnow())

      渲染当前时间:

    {% block scripts %}{
    { super() }}{
    { moment.include_moment() }}{
    % endblock %}

    The local date and time is {

    { moment(current_time).format('LLL') }}.

    That was {

    { moment(current_time).fromNow(refresh=True) }}

    {
    {moment.lang("zh-CN")}}//设置语言{
    {moment().format('YYYY-MM-DD,h:mm:ss a')}}//设置时间格式常用格式化参数YYYY 2014 年份YY 14 2个字符表示的年份Q 1..4 季度M MM 4..04 月份MMM MMMM 4月..四月 根据moment.locale()中的设置显示月份D DD 1..31 一月中的第几天Do 1日..31日 一月中的第几天DDD DDDD 1..365 一年中的第几天X 1410715640.579 时间戳x 1410715640579 时间戳

      flask表单处理

    pip install flask-wtf

      为了实现 CSRF 保护, Flask-WTF 需要程序设置一个密钥。 Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法 

    app = Flask(__name__)app.config['SECRET_KEY'] = 'hard to guess string'

      定义表单类

    from flask.ext.wtf import Formfrom wtforms import StringField, SubmitFieldfrom wtforms.validators import Requiredclass NameForm(Form):    name = StringField('What is your name?', validators=[Required()])    submit = SubmitField('Submit')

      这点flask-wtf要比django的form强大,类似于model form的功能。

      flash一般写在逻辑函数中,比如登陆信息改变时作为提醒在页面输出。在前端页面flask开放函数get_flashed_messages给模板使用,用于取出flash信息。

      例,两次登陆的人姓名改变了,使用flash进行提示。

    @app.route('/',methods=['GET','POST'])def index():    form = NameForm()    if form.validate_on_submit():        old_name=session.get('name')        if old_name is not None and old_name != form.name.data:            flash('name has been changed')            return redirect(url_for('index'))        session['name']=form.name.data        return render_template('index2.html',form=form)    return render_template('index2.html',form=form)
    views.py
    {
    {form.hidden_tag()}}

    {

    {form.name.label}}

    {
    {form.name()}}
    {
    {form.submit }}
    flashed message

    {

    % for message in get_flashed_messages() %} {
    { message }} {
    % endfor %}

    index2.html

     

      使用 Flask-Bootstrap方式渲染

    {% import "bootstrap/wtf.html" as wtf %}{
    { wtf.quick_form(form) }}

      wtf.quick_form() 函数的参数为 Flask-WTF 表单对象,使用 Bootstrap 的默认样式渲染传入的表单。

      用户提交表单后, 服务器收到一个包含数据的 POST 请求。 validate_on_submit() 会调用name 字段上附属的 Required() 验证函数。如果名字不为空,就能通过验证, validate_on_submit() 返回 True。现在,用户输入的名字可通过字段的 data 属性获取。

      Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用 SQLAlchemy 的操作。SQLAlchemy 是一个很强大的关系型数据库框架, 支持多种数据库后台。 SQLAlchemy 提供了高层 ORM,也提供了使用数据库原生 SQL 的低层功能。

    pip install flask-sqlalchemy

       配置数据库

    from flask.ext.sqlalchemy import SQLAlchemybasedir = os.path.abspath(os.path.dirname(__file__))app = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] =\'sqlite:///' + os.path.join(basedir, 'data.sqlite')app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = Truedb = SQLAlchemy(app)

       db 对象是 SQLAlchemy 类的实例,表示程序使用的数据库,同时还获得了 Flask-SQLAlchemy提供的所有功能。

       定义数据库模型

    class Role(db.Model):    __tablename__ = 'roles'    id = db.Column(db.Integer, primary_key=True)    name = db.Column(db.String(64), unique=True)    def __repr__(self):        return '
    ' % self.name

       创建数据库

    (venv) $ python hello.py shell>>> from hello import db>>> db.create_all()

      删除数据库

    db.drop_all()

      增加行数据

    >>> from hello import Role, User>>> admin_role = Role(name='Admin')>>> mod_role = Role(name='Moderator')>>> user_role = Role(name='User')>>> user_john = User(username='john', role=admin_role)>>> user_susan = User(username='susan', role=user_role)>>> user_david = User(username='david', role=user_role)

      此时,我们的对象还只是自己意淫出来的,并没有往数据库中提交,我们需要通过数据库会话管理对数据库做改动 。

      第一步,将要提交对象加入会话中:

    >>> db.session.add(admin_role)>>> db.session.add(mod_role)>>> db.session.add(user_role)>>> db.session.add(user_john)>>> db.session.add(user_susan)>>> db.session.add(user_david)

      或者:

    db.session.add_all([admin_role, mod_role, user_role,user_john, user_susan, user_david])

      为了把对象写入数据库,我们要调用 commit() 方法提交会话:

    db.session.commit()

      此时,我们的对象已经真正的存在数据库中,可以查询到了。

       数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误, 整个会话都会失效。

      修改数据行

    >>> admin_role.name = 'Administrator'>>> db.session.add(admin_role)>>> db.session.commit()

      删除数据库

    >>> db.session.delete(mod_role)>>> db.session.commit()

      查询行数据

      Flask-SQLAlchemy 为每个模型类都提供了 query 对象。最基本的模型查询是取回对应表中的所有记录:

    >>> Role.query.all()[
    ,
    ]>>> User.query.all()[
    ,
    ,
    ]

      过滤查询

    >>> User.query.filter_by(role=user_role).all()[
    ,
    ]

      如果你想看他在底层到底执行的sql语句是什么。

    >>> str(User.query.filter_by(role=user_role))'SELECT users.id AS users_id, users.username AS users_username,users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'

      为了避免一直重复导入,我们可以做些配置,让 Flask-Script shell 命令自动导入特定的对象。

    from flask.ext.script import Shelldef make_shell_context():    return dict(app=app, db=db, User=User, Role=Role)manager.add_command("shell", Shell(make_context=make_shell_context))

       使用Flask-Migrate实现数据库迁移

      使用Flsak-Migrate数据库迁移框架,可以保证数据库结构在发生变化时,改变数据库结构不至于丢失数据库的数据。

      首先第一步,创建数据仓库

      配置 Flask-Migrate 

    from flask.ext.migrate import Migrate, MigrateCommandmigrate = Migrate(app, db)#创建一个Migrate对象并关联对应的应用程序类对象app和数据库管理类对象dbmanager.add_command('db', MigrateCommand)

      在维护数据库迁移之前,要使用 init 子命令创建迁移仓库:

    python hello.py db init

      'db'是在manager.add_command('db',MigrateComand)这句中我们声明的命令行对象名称,init是Migrate命令,表示初始化迁移仓库,运行完成之后,会在当前目录下创建一个migrations的文件夹,用于进行迁移的数据库脚本都放在这里。

      使用migarate子命令来创建数据库迁移脚本,在此之前我们先改动一下数据库的模型来验证迁移是否成功,我们在User模型中添加age属性。

      添加好字段后,使用下面命令自动添加迁移脚本

    python flask_blog.py db migrate

      upgrade() 函数把迁移中的改动应用到数据库中, downgrade() 函数则将改动删除。 

    python flask_blog.py db upgrade

     

       在视图函数中处理数据库管理。

    @app.route('/',methods=['GET','POST'])def index():    form = NameForm()    if form.validate_on_submit():        user = User.query.filter_by(name=form.name.data).first()        if user is None:            user=User(name=form.name.data)            db.session.add(user)#提交数据        session['name']=form.name.data        form.name.data=''        return redirect(url_for('index'))    return render_template('index.html',form=form,name=session['name'])
    View
    Hello {
    % if name %}{
    {name}} {% else %} stranger {% endif%} {
    {form.hidden_tag()}}

    {

    {form.name.label}}

    {
    {form.name()}}
    {
    {form.submit }}
    html

       这里数据提交与shell中不同,我们设置到配置信息里了。

    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True

      之后我们的数据都会自动被提交了。

    转载于:https://www.cnblogs.com/Jeffding/p/8313263.html

    你可能感兴趣的文章
    WebSocket & websockets
    查看>>
    openssl 升级
    查看>>
    ASP.NET MVC:通过 FileResult 向 浏览器 发送文件
    查看>>
    CVE-2010-2883Adobe Reader和Acrobat CoolType.dll栈缓冲区溢出漏洞分析
    查看>>
    使用正确的姿势跨域
    查看>>
    AccountManager教程
    查看>>
    Android学习笔记(十一)——从意图返回结果
    查看>>
    算法导论笔记(四)算法分析常用符号
    查看>>
    ultraedit激活
    查看>>
    总结(6)--- python基础知识点小结(细全)
    查看>>
    亿级曝光品牌视频的幕后设定
    查看>>
    ARPA
    查看>>
    JSP开发模式
    查看>>
    我的Android进阶之旅------&gt;Android嵌入图像InsetDrawable的使用方法
    查看>>
    Detours信息泄漏漏洞
    查看>>
    win32使用拖放文件
    查看>>
    Android 动态显示和隐藏软键盘
    查看>>
    raid5什么意思?怎样做raid5?raid5 几块硬盘?
    查看>>
    【转】how can i build fast
    查看>>
    null?对象?异常?到底应该如何返回错误信息
    查看>>