Flask web開(kāi)發(fā)-基于Python的Web應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)-MacOS

第一章 安裝

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.虛擬環(huán)境

$ virtualenv venv  //創(chuàng)建虛擬環(huán)境
$ source venv/bin/activate  //啟動(dòng)虛擬環(huán)境
$ deactivate //關(guān)閉虛擬環(huán)境

4.需要安裝的Flask 相關(guān)的包:

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

5.python 命令行:

$ python
>>> import flask   //檢查某個(gè)包是否安裝好
>>> exit()  //退出python命令行

第二章 程序的基本結(jié)構(gòu)

Web 服務(wù)器使用一種名為 Web 服務(wù)器網(wǎng)關(guān)接口
(Web Server Gateway Interface,WSGI)的協(xié)議,把接收自客戶端的所有請(qǐng)求都轉(zhuǎn)交給這個(gè)對(duì)象處理。

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 的慣常用法,在這里確保直接執(zhí)行這個(gè)腳本時(shí)才啟動(dòng)開(kāi)發(fā)Web 服務(wù)器。如果這個(gè)腳本由其他腳本引入,程序假定父級(jí)腳本會(huì)啟動(dòng)不同的服務(wù)器,因此不會(huì)執(zhí)行 app.run()

python hello.py  //啟動(dòng)程序
Ctrl + C //退出程序

然后瀏覽器訪問(wèn)
http://127.0.0.1:5000/

請(qǐng)求上下文

請(qǐng)求上下文.png
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()
路由調(diào)度

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:注冊(cè)一個(gè)函數(shù),在處理第一個(gè)請(qǐng)求之前運(yùn)行。
? before_request:注冊(cè)一個(gè)函數(shù),在每次請(qǐng)求之前運(yùn)行。
? after_request:注冊(cè)一個(gè)函數(shù),如果沒(méi)有未處理的異常拋出,在每次請(qǐng)求之后運(yùn)行。
? teardown_request:注冊(cè)一個(gè)函數(shù),即使有未處理的異常拋出,也在每次請(qǐng)求之后運(yùn)行
響應(yīng)

Flask將視圖函數(shù)的返回值,作為響應(yīng)結(jié)果,通常就是一個(gè)字符串
也可以接受第二個(gè)參數(shù),為響應(yīng)的狀態(tài)碼,例如200,404等
也可以接受第三個(gè)參數(shù),是一個(gè)由首部(header)組成的字典

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

當(dāng)然,更好的選擇是返回一個(gè)respones對(duì)象

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

有一種特殊的響應(yīng),叫做重定向(14章會(huì)有例子)

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

還有一種特殊的響應(yīng),由abort()函數(shù)生成,用來(lái)處理錯(cuò)誤
abort 不會(huì)把控制權(quán)交還給調(diào)用它的函數(shù),而是拋出異常把控制權(quán)交給 Web 服務(wù)器

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擴(kuò)展
$ pip install flask-script

文件的改動(dòng):

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

這個(gè)擴(kuò)展的初始化方法也適用于其他很多擴(kuò)展:把程序?qū)嵗鳛閰?shù)傳給構(gòu)造函數(shù),初始化主類(lèi)的實(shí)例。創(chuàng)建的對(duì)象可以在各個(gè)擴(kuò)展中使用。在這里,服務(wù)器由 manager.run() 啟動(dòng),啟動(dòng)后就能解析命令行了。

python hello.py runserver --host 0.0.0.0  //監(jiān)聽(tīng)所有窗口

第三章 模板

面對(duì)一個(gè)請(qǐng)求,其實(shí)server做了兩件事情,分成‘業(yè)務(wù)邏輯’和‘表現(xiàn)邏輯’

例如用戶輸入了賬號(hào)密碼,點(diǎn)擊登錄
業(yè)務(wù)邏輯:在檢查賬號(hào)密碼是否匹配,如果匹配成功則登錄
表現(xiàn)邏輯:跳轉(zhuǎn)到已經(jīng)登錄用戶的首頁(yè)

Jinja2模板引擎

Jinja有兩種定界符。{% ... %}和{{ ... }}。前者用于執(zhí)行像for循環(huán)或賦值等語(yǔ)句,后者向模板輸出一個(gè)表達(dá)式的結(jié)果。
參見(jiàn)http://jinja.pocoo.org/docs/templates/#synopsis

Jinja2變量過(guò)濾器.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>

單獨(dú)文件導(dǎo)入

{% 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中傳進(jìn)來(lái)的參數(shù)

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

hello.py:初始化 Flask-Bootstrap

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

doc 整個(gè) HTML 文檔
html_attribs <html> 標(biāo)簽的屬性
html <html> 標(biāo)簽中的內(nèi)容
head <head> 標(biāo)簽中的內(nèi)容
title <title> 標(biāo)簽中的內(nèi)容
metas 一組 <meta> 標(biāo)簽
styles 層疊樣式表定義
body_attribs <body> 標(biāo)簽的屬性
body <body> 標(biāo)簽中的內(nèi)容
navbar 用戶定義的導(dǎo)航條
content 用戶定義的頁(yè)面內(nèi)容
scripts 文檔底部的 JavaScript 聲明

鏈接
url_for('index')             //得到相對(duì)路徑,這里會(huì)得到'/'
url_for('index', _external=True)       //得到絕對(duì)路徑,這里是'http://localhost:5000/'。(外部使用的時(shí)候要用,例如郵件)
url_for('user', name='john', _external=True)   //返回結(jié)果是 http://localhost:5000/user/john。
url_for('index', page=2)   // 返回結(jié)果是 /?page=2
使用Flask-Moment本地化日期和時(shí)間

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 傳入模板進(jìn)行渲染

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 表示顯示的內(nèi)容,之前錯(cuò)誤的放在了{(lán)% block scripts %}中,發(fā)現(xiàn)不能在頁(yè)面顯示
<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 能保護(hù)所有表單免受跨站請(qǐng)求偽造(Cross-Site Request Forgery,CSRF)的攻擊
hello.py

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
 $ pip install flask-wtf
WTForms支持的HTML字段類(lèi)型.png
WTForms驗(yàn)證函數(shù).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()    //建一個(gè)M
 if form.validate_on_submit():
   name = form.name.data
   form.name.data = ''
 return render_template('index.html', form=form, name=name)  //把得到的數(shù)據(jù)傳給T

MTV:
M是Model,數(shù)據(jù)結(jié)構(gòu),
T是template,模板,最后渲染出來(lái)的頁(yè)面的樣子
V是view,先建一個(gè)M,然后通過(guò)一系列的邏輯和運(yùn)算,得到需要的數(shù)據(jù),傳給T

注:這里我寫(xiě)的時(shí)候出現(xiàn)了三個(gè)錯(cuò)誤

app.config['SECRET_KEY'] = 'hard to guess string'  //這句忘記加了
{{ wtf.quick_form(form) }}  //寫(xiě)成了{(lán)% wtf.quick_form(form) %}
name = form.name.data    //寫(xiě)成了 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表單

項(xiàng)目結(jié)構(gòu)


最基礎(chǔ)的目錄結(jié)構(gòu)

工廠函數(shù)

app/__init__.py :

  bootstrap = Bootstrap()
  mail = Mail()
  moment = Moment()
  db = SQLAlchemy()

def create_app(config_name):
    app = Flask(__name__)   //實(shí)例化一個(gè)Flask對(duì)象,命名為app
    app.config.from_object(config[config_name])  //把我們配置的config,設(shè)置給這個(gè)app
    config[config_name].init_app(app)  //config中定義的初始化函數(shù)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    from .main import main as main_blueprint //在app中,注冊(cè)blueprint
    app.register_blueprint(main_blueprint)

    return app

blueprint

app/main/__init__.py:創(chuàng)建藍(lán)本:

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

實(shí)例化一個(gè)藍(lán)本,命名為main,接受兩個(gè)參數(shù),第一個(gè)是藍(lán)本的名字,第二個(gè)是藍(lán)本所在的包或模塊,一般都是__name__
views(路由設(shè)定),errors(錯(cuò)誤處理)必須在創(chuàng)建藍(lán)本之后導(dǎo)入,以防止循環(huán)依賴(lài)。

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('/'),有了藍(lán)本 以后,路由都是由藍(lán)本提供,所以前面要加上藍(lán)本的名字
@main.route('/', methods=['GET', 'POST'])。
2)url_for 不再像單文件的url_for('index'),現(xiàn)在需要指明相應(yīng)的藍(lán)本url_for('main.index')
url_for實(shí)際上就是根據(jù)后面的參數(shù)來(lái)生成url的,以便程序跳轉(zhuǎn),所以一般都這樣用redirect(url_for('.index')) 
3)藍(lán)本的端點(diǎn)都是有命名空間的,其實(shí)就是藍(lán)本的名字,這樣可以避免沖突。例如main.index和auth.index就是兩個(gè)頁(yè)面,可以同時(shí)存在。
4)url_for('main.index')可以簡(jiǎn)寫(xiě)成url_for('.index'),這里省略的命名空間就是當(dāng)前請(qǐng)求所在的藍(lán)本,例如現(xiàn)在的main,跨藍(lán)本的重定向必須帶有命名空間的端點(diǎn)名。

7.5 需求文件

(venv) $ pip freeze >requirements.txt     //生成需求文件
(venv) $ pip install -r requirements.txt    //使用需求文件去安裝各種包
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容