Python Flask Web 項目實戰


一、基礎

1,首先是虛擬環境的配置

pip install virtualenv -i https://pypi.doubanio.com/simple  # 豆瓣源安裝虛擬環境
mkdir falsk-venv
cd falsk-venv
virtualenv venv  # 在當前目錄下床架一個目錄,表示虛擬環境的目錄名為venv,包含了Python可執行文件,以及pip庫的一個備份

當然如果本機存在多個版本的Python,可以選擇一個Python,可以選擇一個Python解釋器,在指定之前,必須將flask-venv目錄下整個文件夾都刪掉再使用

virtualenv -p C:Python27\python.exe venv  # -p 用來指定Python解釋器程序的路徑

那如何激活使用虛擬環境呢?

在cmd里面輸入

dir
activate  # 激活虛擬環境
deactivate  # 退出虛擬環境
rmvirtualenv flask-venv  # 刪除虛擬環境

2,Flask 快速上手

 使用pycharm來創建

from flask import Flask

app = Flask(__name__)


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


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

有人會問啊,為什么非要加這個

if __name__ == '__main__':

其實原因是在python中,所有沒有縮進的代碼都會被執行,__name__是Python的內建函數,指的是當前模塊的名稱,,每個模塊都有這個屬性,但__name__是可以變化的,如果某個模塊直接被運行那么__name__ = '__main__', 條件為真,但是當模塊被導入的時候就為假,也就是app.run()不會被執行。

 

和Django不同的是flask的路由是以裝飾器的形式加載所需要執行視圖函數的上方

調試:

django的開啟調試是在settings中的DEBUG=True,而flask簡單很多,在app.run()中加入debug=True即可

from flask import Flask

app = Flask(__name__)


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


if __name__ == '__main__':
    app.run(debug=True)  # 開啟調試

flask開啟調試好處是什么呢?

1,遇到程序有bug,會在控制台輸出具體的錯誤信息,否則只會報籠統的應用服務器錯誤。

2,自動重啟

當然app.run中可以加的參數還有很多,比如設定host,port

另外說一句:默認的port就是5000

from flask import Flask

app = Flask(__name__)


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


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

二、URL傳遞參數

Flask傳遞參數的語法是'/參數/'

注意點:①參數需要放在一對<>②視圖函數中需要設置與URL中相同的參數名

from flask import Flask

app = Flask(__name__)


@app.route('/user/<name>')
def hello_world(name):
    return f'Hello World!{name}'


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

像這個就是沒有指定數據的類型的,那么默認的就是string

from flask import Flask

app = Flask(__name__)


@app.route('/user/<int:id>')
def hello_world(id):
    return f'Hello World!{id}'


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

像這樣就是指定接收的參數類型為int類型,傳遞參數str或者float都是不行的

三、URL的反轉

我們知道Django的URL反轉模板語言中使用的是這樣{% url ‘name’ %} 通過別名的方式,視圖函數中是這樣

from django.shortcuts import reverse
reverse('article:article_detail', kwargs={'id': article_id, 'slug': slug})

但是在flask中,URL的反轉卻很有意思直接使用url_for

from flask import Flask, url_for

app = Flask(__name__)


@app.route('/user')
def hello_world(id):
    reverse_path = url_for('hello_world')
    return f'Hello World!{reverse_path}'


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

使用URL的反轉,使用的是

from flask import url_for

最簡單的做法即使以視圖函數的名稱作為參數,就像上面例子得到的就是/user

三、模板引擎jinja2

因為jinja2和Django模板引擎基本一樣,所以基礎的就不講了,直接開干

比方說我們隨便在templates目錄下新建一個html文件user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>{{ name }}</div>

</body>
</html>

app.py文件代碼如下:

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/user')
def hello_world():
    name = '先生'
    return render_template('user.html', name=name)


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

和Django的不同的是:Django使用render(request, '模板')來進行返回

Flask通過render_template()函數來實現模板的渲染。要使用jinjia2模板就是必須用

from flask import render_template

當然參數量過多的時候**locals()

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/user')
def hello_world():
    name = '先生'
    return render_template('user.html', **locals())


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

而Django不要加**

上面的都是太簡單了,整點有難度的,舉個栗子,自定義過濾器(本質上過濾器就是轉換函數)

我們都知道django的過濾器需要在各自的app下新建一個templatetags,然后在模板中導入縮寫的py文件

那么Flask怎么做的呢?

通過調用add_template_filter方法來實現。該方法第一個參數是函數名,第二個參數是自定義過濾器的名稱。

user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>{{ name | index_class }}</div>

</body>
</html>

app.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/user', methods=['GET', 'POST'])
def hello_world():
    name = '先生'
    return render_template('user.html', **locals())


def do_index_class(index):  # 定義函數
    return index + '牛逼****'


app.add_template_filter(do_index_class, 'index_class')


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8000)

對於如果post上傳文件

就需要使用

f = request.files['filesname'] 和django類似

f.save(‘path’,‘name’)即可

宏的定義及使用:

Jinja 2中的宏功能類似Python中的函數,可以傳參,但是不能有返回值。我們可以將一些經常用到的代碼片段放在宏中,然后把一些不固定的值抽取出來作為一個變量。

宏的聲明:

<!--定義宏-->
    {% macro input (name, type='text', value='') -%}
        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
    {%- endmacro  %}

上方的代碼定義了一個宏,定義宏要加macro,結束要加上endmacro標志。宏的名稱時input

他們有三個參數,分別是name、type、value。后兩個參數有默認值。我們可以試用表達式來調用這個宏

{{ input('username') }}
{{ input('password', type='password') }}

看一個完整的實例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>{{ name | index_class }}</div>
<div>
    <!--定義宏-->
    {% macro input (name, type='text', value='') -%}
        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
    {%- endmacro  %}

<p>用戶名:{{ input('username') }}</p>
<p>密碼:{{ input('password', type='password') }}</p>
<p>登錄:{{ input('submit', type='submit', value='登錄') }}</p>

</div>

</body>
</html>

宏的導入:

一個宏可以被不同的模板使用,因此我們想到了一個東西,抽取為公共模塊,在需要使用的時候導入即可

導入的方法呢也和Python類似,import嘛

因為我們在上面已經寫了一個宏了

那我們就導入

方式1:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% import 'user.html' as user %}
<p>用戶名:{{ user.input('username') }}</p>
<p>密碼:{{ user.input('password', type='password') }}</p>
<p>登錄:{{ user.input('submit', type='submit', value='登錄') }}</p>
</body>
</html>

方式二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% from 'user.html' import input %}
<p>用戶名:{{ input('username') }}</p>
<p>密碼:{{ input('password', type='password') }}</p>
<p>登錄:{{ input('submit', type='submit', value='登錄') }}</p>
</body>
</html>

效果是完全一樣的

include的使用:

和django的include類似:

宏文件可以引用其他宏,可以使用include語句可以把一個模板引入到另一個模板中,類似於把一個模板的代碼復制到另外一個模板的指定位置。

footer.hrml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="footer">這是網頁尾部</div>

</body>
</html>

header.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="header">這是網頁頭部</div>

</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .header {
            width: 100%;
            height: 40px;
            margin: 20px 20px;
        }
        .footer {
            width: 100%;
            height: 40px;
            margin: 20px 20px;
        }
        .content {
            width: 100%;
            height: 40px;
            margin: 20px 20px;
        }
    </style>
</head>
<body>
{% from 'user.html' import input %}
<p>用戶名:{{ input('username') }}</p>
<p>密碼:{{ input('password', type='password') }}</p>
<p>登錄:{{ input('submit', type='submit', value='登錄') }}</p>
{% include "header.html" %}
<div class="content">這是網頁內容</div>
{% include "footer.html" %}
</body>
</html>

頁面效果:

 

 set 和with語句的使用:

set語句是在模板中定義變量並賦予值,在整個模板范圍都有效

with關鍵字是在定義變量並賦值的同事,限制with定義變量的作用范圍

{% set a = '上山打老虎' %}
{% set b = ['上山打老虎', ('天青色等煙雨', '而我在等你')] %}
{{ a }}
{{ b }}
{% with bat = 99 %}
    {{ bat }}
{% endwith %}

 

 靜態文件的加載:

<script type="text/javascript" src="static/js/jquery-3.3.1/jquery-3.3.1.js"></script>
<script src="{{ url_for('static', filename='js/jquery-3.3.1/jquery-3.3.1.js') }}"></script>
<script src="{{ url_for('static', filename='images/dog.jpg') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/dog.css') }}">

推薦使用{{url_for}}的方法

模板的繼承:

和Django一樣,在需要繼承的子模板中extends即可

父模板user.html

{% block baby %}
{% endblock %}

子模版

{% extends 'user.html' %}
 {% block baby %}
     {{ super() }}
{% endblock %}

注意:如果想在一個block中調用其他的block代碼可以通過{{self.其他block名稱()}}實現

{% self.baby() %}

四、Flask視圖高級技術

 在Flask框架中,默認使用@app.route裝飾器:類似這樣

@app.route('/user')
def hello_world():
    name = '先生'
    return render_template('user.html', **locals())

上述代碼中,視圖函數為hello_word是如此建立URL和視圖之間的關系的

 我們想要反轉得到url,url_for("hello_word")反轉得到URL,實際上flask可以像Django一樣添加別名,那就是用endpoint參數

@app.route('/user', endpoint='hello')
def hello_world():
    name = '先生'
    return render_template('index.html', **locals())

一旦我們加上endpoint參數,在使用url_for()反轉時就不能使用視圖函數名了,而要使用我們定義的URL別名

那這個app.route的裝飾器本質是什么呢?

我們看一下源碼

 

 那么這個add_url_rule怎么使用呢?

換句話說,如何通過add_url_rule方法來實現和上面相同的效果呢?

# @app.route('/user', endpoint='hello')
def hello_world():
    name = '先生'
    return render_template('index.html', **locals())


app.add_url_rule('/user', endpoint='hello', view_func=hello_world)

# endpoint參數依舊可以不填,但是view_func必須是對應的函數名

類視圖:

那我們可能會想,既然裝飾器更加方便為什么還需要用add_url_rule方法?

因為類的視圖函數需要通過其來進行注冊

flask的類視圖一般分為標准類視圖,基於調度方法的類視圖

 

①標准類視圖:

1, 必須繼承flask.views.View

2, 必須實現dispatch_request方法,請求過來之后,都會執行這個方法。這個方法的返回值相當於之前的視圖函數,也必須返回Response或者子類的對象,或者是字符串、元祖

3, 必須通過app.add_rule(rule, endpoint, view_func)來做URL與視圖的映射。view_func參數需要使用as_view類方法來轉換

4, 如果指定endpoint,url_for反轉時必須使用別名。反之則使用as_view(視圖名稱)指定的視圖名稱來作為反轉

 

優點:

使用類視圖的好處是支持繼承,可以把一些共性的東西放在父類中,其他子類可以繼承,但是類視圖不能和函數視圖一樣,需要通過app.add_url_rule來注冊

 

 

例子:

網站里面需要在多個不同頁面都放置一個廣告,使用類視圖函數如何實現?

from flask import Flask, render_template, views

app = Flask(__name__)


class Bgs(views.View):
    def __init__(self):
        super().__init__()
        self.context = {
            "bgs": "我是固定牛皮廯"
        }


class Index(Bgs):
    def dispatch_request(self):  # 使用dispatch_request()方法,定義類視圖函數
        return render_template('index.html', **self.context)


class Index2(Bgs):
    def dispatch_request(self):  # 使用dispatch_request()方法,定義類視圖函數
        return render_template('index.html', **self.context)


# 定義view_func必須經過as_view方法
app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('Index'))
app.add_url_rule(rule='/index2', endpoint='index2', view_func=Index2.as_view('Index2'))

②基於方法的類視圖:

利用視圖函數實現不同請求不同的邏輯時比較復雜,和django的類視圖方法類似。也就是flask.views.MethodView,對每一個Http方法執行不同的函數

from flask import Flask, render_template, views, request

app = Flask(__name__)


@app.route('/user', endpoint='hello')
def hello_world():
    name = '先生'
    return render_template('index.html', **locals())


class LoginView(views.MethodView):  # 定義LoginView類
    def get(self):
     name = request.args['username']
return render_template("index.html") def post(self): username = request.form.get("username") password = request.form.get("password") if username == 'zhou' and password == '123': return "登陸成功" else: return "登錄失敗" app.add_url_rule('/login', endpoint='hello', view_func=LoginView.as_view('loginview'))

Flask的藍圖:

flask為了降低模塊之間的耦合性,設計了藍圖

優點: 可以極大的簡化大型應用,並為擴展提供集中的注冊入口。Blueprint對象與Flask應用對象的工作方式類似,但是不是一個真正的應用

舉個栗子:

新建三個文件,分別為app.py b.py c.py

from flask import Flask, render_template
import b, c

app = Flask(__name__)


@app.route('/user', endpoint='hello')
def hello_world():
    name = '先生'
    return render_template('index.html', **locals())


app.register_blueprint(b.b_list)  # 將b模塊中的b_list注冊到app
app.register_blueprint(c.c_list)  # 將c模塊中的c_list注冊到app

b.py

# -*- coding:UTF-8 -*-
__autor__ = 'zhouli'
__date__ = '2019/11/3 18:14'

from flask import Blueprint
b_list = Blueprint("b", __name__)
"""
創建一個Blueprint對象,第一個參數可以當做改Blueprint對象的姓名
在一個app中,姓名不能與其余的Blueprint對象姓名重復
第二個參數用於初始化
"""


@b_list.route("/b")
def b():
    return "b"

藍圖的主要目的:實現各個模塊的視圖函數寫在不同的py文件中。在主視圖中導入分路視圖的模塊,並且注冊藍圖對象。

 

 

Flask處理表單:

 1,使用flask處理通用表單

pip install flask-wtf
# Flask-WTF 提供對所有Form表單免受跨站請求偽造(CSRF)

②在flask根目錄下面新增config.py配置文件,要啟用CSRF保護,需要定義兩個變量

CSRF_ENABLED = True
SECRET_KEY = ‘x1x2x3x4x5x6’  # 自定義

其中SECRET_KEY用來建立加密的令牌,用於驗證Form表單的提交,在自己編寫對應的應用程序的時候,可以設置的較為復雜些,那這樣攻擊者就難以猜到密鑰值

在app.py中添加

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM