美文网首页
Flask web开发-基于Python的Web应用开发实战-M

Flask web开发-基于Python的Web应用开发实战-M

作者: 姚大宝Svan | 来源:发表于2017-04-29 23:40 被阅读153次

    第一章 安装

    1.安装virtualenv

    $ pip install virtualenv  // or $ sudo easy_install virtualenv 
    $ virtualenv --version
    

    2.Clone source code to local

    $ cd flask-2017   //create a folder named flask-2017
    $ git clone  git@github.com:baby4bamboo/flasky.git 
    $ cd flasky
    $ git checkout 1a
    

    3.虚拟环境

    $ virtualenv venv  //创建虚拟环境
    $ source venv/bin/activate  //启动虚拟环境
    $ deactivate //关闭虚拟环境
    

    4.需要安装的Flask 相关的包:

    $ pip install flask //MarkupSafe, Jinja2, click, Werkzeug, itsdangerous, flask
    
    

    5.python 命令行:

    $ python
    >>> import flask   //检查某个包是否安装好
    >>> exit()  //退出python命令行
    

    第二章 程序的基本结构

    Web 服务器使用一种名为 Web 服务器网关接口
    (Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。

    git checkout 2b

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return '<h1>Hello World!</h1>'
    
    @app.route('/user/<name>')
    def user(name):
        return '<h1>Hello, %s!</h1>' % name
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    __name__=='__main__'是 Python 的惯常用法,在这里确保直接执行这个脚本时才启动开发Web 服务器。如果这个脚本由其他脚本引入,程序假定父级脚本会启动不同的服务器,因此不会执行 app.run()

    python hello.py  //启动程序
    Ctrl + C //退出程序
    

    然后浏览器访问
    http://127.0.0.1:5000/

    请求上下文

    请求上下文.png
    >>> app_ctx = app.app_context()
    >>> app_ctx.push()
    >>> current_app.name
    'hello'
    >>> app_ctx.pop()
    
    路由调度

    Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。

     $ python
    >>> from hello import app
    >>> app.url_map
    Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
     <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
     <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
    
    Hook
    • before_first_request:注册一个函数,在处理第一个请求之前运行。
    • before_request:注册一个函数,在每次请求之前运行。
    • after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
    • teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行
    
    响应

    Flask将视图函数的返回值,作为响应结果,通常就是一个字符串
    也可以接受第二个参数,为响应的状态码,例如200,404等
    也可以接受第三个参数,是一个由首部(header)组成的字典

    @app.route('/')
    def index():
     return '<h1>Bad Request</h1>', 400
    

    当然,更好的选择是返回一个respones对象

    from flask import make_response
    @app.route('/')
    def index():
     response = make_response('<h1>This document carries a cookie!</h1>')
     response.set_cookie('answer', '42')
     return response
    

    有一种特殊的响应,叫做重定向(14章会有例子)

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

    还有一种特殊的响应,由abort()函数生成,用来处理错误
    abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器

    from flask import abort
    @app.route('/user/<id>')
    def get_user(id):
     user = load_user(id)
     if not user:
     abort(404)
     return '<h1>Hello, %s</h1>' % user.name
    
    Flask扩展
    $ pip install flask-script
    

    文件的改动:

    from flask.ext.script import Manager
    manager = Manager(app)
    # ...
    if __name__ == '__main__':
     manager.run()
    

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

    python hello.py runserver --host 0.0.0.0  //监听所有窗口
    

    第三章 模板

    面对一个请求,其实server做了两件事情,分成‘业务逻辑’和‘表现逻辑’

    例如用户输入了账号密码,点击登录
    业务逻辑:在检查账号密码是否匹配,如果匹配成功则登录
    表现逻辑:跳转到已经登录用户的首页

    Jinja2模板引擎

    Jinja有两种定界符。{% ... %}和{{ ... }}。前者用于执行像for循环或赋值等语句,后者向模板输出一个表达式的结果。
    参见http://jinja.pocoo.org/docs/templates/#synopsis

    Jinja2变量过滤器.png

    if

    {% if user %}
     Hello, {{ user }}!
    {% else %}
     Hello, Stranger!
    {% endif %}
    

    for

    <ul>
     {% for comment in comments %}
     <li>{{ comment }}</li>
     {% endfor %}
    </ul>
    

    macro

    {% macro render_comment(comment) %}
     <li>{{ comment }}</li>
    {% endmacro %}
    <ul>
     {% for comment in comments %}
     {{ render_comment(comment) }}
     {% endfor %}
    </ul>
    

    单独文件导入

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

    模板继承

    base.html

    <html>
    <head>
     {% block head %}
     <title>{% block title %}{% endblock %} - My Application</title>
     {% endblock %}
    </head>
    <body>
     {% block body %}
     {% endblock %}
    </body>
    </html>
    

    extend.html

    {% extends "base.html" %}
    {% block title %}Index{% endblock %}
    {% block head %}
     {{ super() }}
     <style>
     </style>
    {% endblock %}
    {% block body %}
    <h1>Hello, World!</h1>
    {% endblock %}
    
    模板渲染

    <h1>Hello, {{ name }}!</h1>

    @app.route('/user/<name>')
    def user(name):
     return render_template('user.html', name=name)
    

    name=name,左边的name表示模板中的占位符,右边的name表示url中传进来的参数

    使用Flask-Bootstrap集成Twitter Bootstrap
     $ pip install flask-bootstrap
    

    hello.py:初始化 Flask-Bootstrap

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

    doc 整个 HTML 文档
    html_attribs <html> 标签的属性
    html <html> 标签中的内容
    head <head> 标签中的内容
    title <title> 标签中的内容
    metas 一组 <meta> 标签
    styles 层叠样式表定义
    body_attribs <body> 标签的属性
    body <body> 标签中的内容
    navbar 用户定义的导航条
    content 用户定义的页面内容
    scripts 文档底部的 JavaScript 声明

    链接
    url_for('index')             //得到相对路径,这里会得到'/'
    url_for('index', _external=True)       //得到绝对路径,这里是'http://localhost:5000/'。(外部使用的时候要用,例如邮件)
    url_for('user', name='john', _external=True)   //返回结果是 http://localhost:5000/user/john。
    url_for('index', page=2)   // 返回结果是 /?page=2
    
    使用Flask-Moment本地化日期和时间

    1.安装

    pip install flask-moment
    

    2.hello.py 中初始化Moment:

    from flask.ext.moment import Moment
    moment = Moment(app)
    

    3.基模板(templates/base.html)中引入moment.js

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

    4.把变量current_time 传入模板进行渲染

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

    5.在模板中渲染 current_time
    templates/index.html

    {% block page_content %} //page_content 表示显示的内容,之前错误的放在了{% block scripts %}中,发现不能在页面显示
    <p>The local date and time is {{ moment(current_time).format('LLL') }}</p>
    <p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
    {% endblock %}
    

    第四章 web表单

    Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击
    hello.py

    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'hard to guess string'
    
     $ pip install flask-wtf
    
    WTForms支持的HTML字段类型.png WTForms验证函数.png

    M

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

    T

    templates/index.html

    {% extends "base.html" %}
    {% import "bootstrap/wtf.html" as wtf %}
    {% block title %}Flasky{% endblock %}
    {% block page_content %}
    <div class="page-header">
     <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
    </div>
    {{ wtf.quick_form(form) }}
    {% endblock %}
    

    V

    hello.py:

    @app.route('/', methods=['GET', 'POST'])
    def index():
     name = None
     form = NameForm()    //建一个M
     if form.validate_on_submit():
       name = form.name.data
       form.name.data = ''
     return render_template('index.html', form=form, name=name)  //把得到的数据传给T
    

    MTV:
    M是Model,数据结构,
    T是template,模板,最后渲染出来的页面的样子
    V是view,先建一个M,然后通过一系列的逻辑和运算,得到需要的数据,传给T

    注:这里我写的时候出现了三个错误

    app.config['SECRET_KEY'] = 'hard to guess string'  //这句忘记加了
    {{ wtf.quick_form(form) }}  //写成了{% wtf.quick_form(form) %}
    name = form.name.data    //写成了 name = form.name.data()
    

    session and 重定向

    from flask import Flask, render_template, session, redirect, url_for
    @app.route('/', methods=['GET', 'POST'])
    def index():
     form = NameForm()
     if form.validate_on_submit():
       session['name'] = form.name.data
       return redirect(url_for('index'))
     return render_template('index.html', form=form, name=session.get('name'))
    

    hello.py flash消息

    from flask import Flask, render_template, session, redirect, url_for, 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('Looks like you have changed your name!')
                 session['name'] = form.name.data
                 return redirect(url_for('index'))
     return render_template('index.html',form = form, name = session.get('name'))
    

    base.html

    {% block content %}
    <div class="container">
      {% for message in get_flashed_messages() %}
      <div class="alert alert-warning">
        <button type="button" class="close" data-dismiss="alert">×
        </button>
        {{ message }}
      </div>
      {% endfor %}
      {% block page_content %}{% endblock %}
    </div>
    {% endblock %}
    

    第七章 web表单

    项目结构


    最基础的目录结构

    工厂函数

    app/__init__.py :

      bootstrap = Bootstrap()
      mail = Mail()
      moment = Moment()
      db = SQLAlchemy()
    
    def create_app(config_name):
        app = Flask(__name__)   //实例化一个Flask对象,命名为app
        app.config.from_object(config[config_name])  //把我们配置的config,设置给这个app
        config[config_name].init_app(app)  //config中定义的初始化函数
    
        bootstrap.init_app(app)
        mail.init_app(app)
        moment.init_app(app)
        db.init_app(app)
    
        from .main import main as main_blueprint //在app中,注册blueprint
        app.register_blueprint(main_blueprint)
    
        return app
    

    blueprint

    app/main/__init__.py:创建蓝本:

    from flask import Blueprint
    main = Blueprint('main', __name__)
    from . import views, errors
    

    实例化一个蓝本,命名为main,接受两个参数,第一个是蓝本的名字,第二个是蓝本所在的包或模块,一般都是__name__
    views(路由设定),errors(错误处理)必须在创建蓝本之后导入,以防止循环依赖。

    app/main/view.py:

    # from ...
    @main.route('/', methods=['GET', 'POST'])
    def index():
        form = NameForm()
        if form.validate_on_submit():
             #...
            return redirect(url_for('.index'))
        return render_template('index.html',form=form, name=session.get('name'),known=session.get('known', False))
    
    1)不再像单文件@route('/'),有了蓝本 以后,路由都是由蓝本提供,所以前面要加上蓝本的名字
    @main.route('/', methods=['GET', 'POST'])。
    2)url_for 不再像单文件的url_for('index'),现在需要指明相应的蓝本url_for('main.index')
    url_for实际上就是根据后面的参数来生成url的,以便程序跳转,所以一般都这样用redirect(url_for('.index')) 
    3)蓝本的端点都是有命名空间的,其实就是蓝本的名字,这样可以避免冲突。例如main.index和auth.index就是两个页面,可以同时存在。
    4)url_for('main.index')可以简写成url_for('.index'),这里省略的命名空间就是当前请求所在的蓝本,例如现在的main,跨蓝本的重定向必须带有命名空间的端点名。
    

    7.5 需求文件

    (venv) $ pip freeze >requirements.txt     //生成需求文件
    (venv) $ pip install -r requirements.txt    //使用需求文件去安装各种包
    

    相关文章

      网友评论

          本文标题:Flask web开发-基于Python的Web应用开发实战-M

          本文链接:https://www.haomeiwen.com/subject/gbqttxtx.html