flask基礎

對Flask感興趣的,能夠看下這個視頻教程:http://study.163.com/course/courseLearn.htm?courseId=1004091002css

1. 第一個 flask 程序

# 從 flask 框架中導入 flask 類
from flask import Flask

# 用 flask() 初始化一個 flask 對象,並賦給 app
# 需傳遞一個參數 __name__
#  1. 方便 flask 框架去尋找資源
#  2. 方便 flask 插件去定位問題
app = Flask(__name__)


# @app.route() 是一個裝飾器,做用是對 url 與 視圖函數進行映射
# 將 `/` 映射到 hello_world() 函數上
# 即用戶訪問 http://example:80/ 的時候,用 hello_world() 函數來響應
@app.route('/')
def hello_world():
    return 'Hello World!'


# 若是當前文件做爲程序入口,那麼就執行 app.run() 
if __name__ == '__main__':
    # app.run() 是啓動一個應用服務器來響應用戶請求並不斷監聽   
    app.run()

2. 使用 debug 模式

使用 debug 模式有不少好處:html

  1. 將報錯信息顯示到瀏覽器上,而不須要進入編輯器中查看報錯信息,方便開發者查看錯誤
  2. 當檢測到程序代碼(.py文件)發生改動,程序會自動加載而不須要手動重啓服務器

對 flask 程序使用 debug 模式有 2 種方式,以下:前端

2.1 在 app.run() 中使用

app.run() 中直接傳入一個關鍵字參數: app.run(debug=True)python

2.2 在配置文件中使用

  1. 在相同目錄下,新建一個 python 文件,建議命名爲 config,並在裏面指定該程序配置了 DEBUG 模式,即 `config.py 文件的內容以下:mysql

    config.py
    
     # encoding:utf-8
     DEBUG = True
     # SECRET_KEY    
     # SQLALCHEMY_DB     # 數據庫的一些參數配置
  2. 而後在主 app 文件中導入這個文件並配置到 app 中,主 app 文件內容以下:jquery

    First_Flask.py
    
     # encoding:utf-8
     from flask import Flask
     import config           # 導入 config 配置文件
    
     app = Flask(__name__)
     app.config.from_object(config)      # 將該配置文件的配置信息應用到 app 中
    
     @app.route('/')
     def hello_world():
         a = 3
         b = 0
         c = a/b
         return 'Hello,World.'
    
     if __name__ == '__main__':
         app.run()
  3. config.py 文件的用處很是大,須要掌握這種配置方法,在後期的 SECRET_KEYSQLALCHEMY_DB (與數據庫有關)都須要在這個文件中作配置.web

3. URL 傳參到視圖

  1. 參數的做用:能夠在相同的 URL 可是指定不一樣的參數時,來加載不一樣的數據

如: http://localhost:8000/article/abchttp://localhost:8000/article/def 中,兩條 URL 的參數不一樣,咱們能夠獲取這個參數並渲染後返回客戶瀏覽器sql

  1. 如何在 flask 中使用參數?代碼以下數據庫

    @app.route('/article/<id>')
     def article(id):
         return u'<h1>你請求的參數是:%s<h1>' % id
    1. 參數須要放置在兩個尖括號中
    2. 視圖函數中須要放和 URL 參數同名的參數

4. URL 反轉

正轉指的是:在獲取到用戶輸入的 URL 後將該 URL 映射到對應的視圖函數中,讓對應的視圖函數去處理該用戶的請求;
反轉指的是:與正轉相反,經過視圖函數來查找對應的 URL。
反轉的做用是:1. 在頁面重定向的時候會使用 URL 反轉;2. 在模板中會使用 URL 反轉編程

實現反轉的方法:

  1. 在 flask 框架中導入 url_for 模塊
  2. url_for('FunctionName') 反轉

  3. First_Flask.py 源碼以下:

    # encoding:utf-8
     from flask import Flask,url_for
     import config
    
     app = Flask(__name__)
     app.config.from_object(config)
    
     @app.route('/')
     def hello_world():
         print url_for('article',id='123')
         print url_for('my_list')
         return 'Hello,World.'
    
     @app.route('/article/<id>/')
     def article(id):
         return u'<h1>你請求的參數是:%s<h1>' % id
    
     @app.route('/list/')
     def my_list():
         return '<h1>list</h1>'
    
     if __name__ == '__main__':
         app.run()

5. 頁面跳轉和重定向

  1. 做用:在用戶訪問某些須要登陸的頁面時,若是用戶沒登陸,則可讓他重定向到登陸頁面

  2. 實現:

    import redirect,url_for
     redirect(url_for('login'))

2、jinja2 模板

1. 模板渲染和參數

1.1 模板渲染

模板實際上就是一些被編寫好的具備必定格式的 html 文件。

在 pycharm 左側一欄,項目下有兩個文件夾: statictemplate,分別用於存放靜態文件(如css,js,img文件)和模板文件(如html),因此咱們的 html 文件應該放在 template 文件夾下。

如何在主程序中調用模板文件呢?

  1. template 文件夾下新建一個 html 文件
  2. 在主程序中導入 render_template 模塊
  3. 調用語法:render_template('abc.html'),注意不用寫路徑,flask 會自動去 template 文件夾下查找 abc.html,但有文件夾除外

代碼以下:

import render_template
return render_template('index.html')

1.2 參數

在 web 項目開發的大多數狀況下,咱們須要在 html 文件中從後臺程序傳入一些參數,而後將帶有這些參數的 html 文件返回瀏覽器。這時候就須要在 html 文件中引用這些後臺的參數,方法是 {{ Params }} 用 2 個花括號括起來,同時還要在後臺程序作一些傳參的動做。具體以下:

  1. 後端傳參:

    render_template('index',username=u'螞蟻有毒',gender=u'男',age=18)
  1. 前端引用:

    <h1>用戶名:{{ username }}</h1>
     <h1>性別:{{ gender }}</h1>
     <h1>年齡:{{ age }}</h1>

可是要是參數愈來愈多,則代碼會變得很複雜,可讀性差,管理難度大。那麼咱們能夠用一個字典(DICT)來定義一組參數。以下:

user = {
    'username':id,
    'gender':u'男',
    'age':18
}

# 調用時傳入一個關鍵字參數便可
return render_template('index.html',**user)

2. 模板中訪問屬性和字典

上面所演示的都是調用一些簡單的參數,若是在更復雜的環境下,如調用類的屬性呢?或者是調用字典中的字典的值呢?應該怎麼作?

  1. 在主程序中能夠先定義一個類並實例化:

    class Person(object){
         name = u'螞蟻有毒'
         gender = u'男'
         age = 18
     }
     p = Person()
  2. 再定義一個字典:

    content = {
         'person':p,
         'websites':{
             'baidu':'www.baidu.com',
             'google':'www.google.com'
         }
     }
  3. 而後傳參時進行調用:

    return render_template('index',**content)
  4. 最後在 index.html 中調用:

    <p>姓名:{{person.name}}</p>
     <p>性別:{{person.gender}}</p>
     <p>年齡:{{person.age}}</p>
     <hr>
     <p>百度:{{websites.baidu}}</p>
     <p>{{websites.google}}</p>

3. 模板中的 if 和 for

實際上,咱們還能夠在模板(html文件)中嵌入python的代碼:{% code %},這是 jinja2 的語法,能夠嵌入 if 語句和 for 語句來在 html 文件中執行相關的邏輯操做。

3.1 if 語句的操做

  1. 主程序代碼:

    # encoding:utf-8
    
     from flask import Flaskrender_template
    
     app = Flask(__name__)
     app.config.from_object(config)
    
     @app.route('/<is_login>')
     def index(is_login):
         if is_login == '1':
             user = {
                 'username':u'螞蟻有毒',
                 'age':18
             }
             return render_template('index.html',user=user)
         else:
             return render_template('index.html')
    
     if __name__ == '__main__':
         app.run(debug=True)
  2. html 代碼:

    <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>螞蟻有毒的首頁</title>
     </head>
     <body>
         <!-- 若是用戶存在而且年齡大於18就顯示用戶名 -->
         {% if user and user.age > 18 %}
             <a href="#">{{ user.username }}</a>
             <a href="#">註銷</a>
         {% else %}
             <a href="#">登陸</a>
             <a href="#">註冊</a>
         {% endif %}
    
         <h1>歡迎來到螞蟻有毒的首頁。</h1>
     </body>
     </html>

3.2 for 語句的操做

3.2.1 基本用法

for 循環的語法,在 html 中調用 for 語句和 if 語句的語法是同樣的,都是在 {% %} 中寫入 for 關鍵字。
咱們能夠藉助 html 中的 for 語句來遍歷一些變量(List or Dict or Truple)。

  1. 主程序代碼:

    users = {
         'username':u'螞蟻有毒',
         'gender':u'男',
         'age':18
     }
     websites = ['www.baidu.com','www.google.com','www.qq.com']
     return render_template('index.html',user=users,website=websites)
  2. html 代碼:

    {% for k,v in user.items() %}
         <p>{{ k }}:{{ v }}</p>
     {% endfor %}
     <hr>
     {% for website in websites %}
         <p>{{ website }}</p>
     {% endfor %}

完整代碼參照上一節

3.2.2 練習

題目:渲染一個 四大名著 給前端,而後前端用一個表格展現出來。

  1. 先在主程序中定義一個變量用於存放四大名著的基本信息:

    books = {
         u'三國演義':{
             'author':u'羅貫中',
             'price':109
         },
         u'西遊記':{
             'author':u'吳承恩',
             'price':120
         },
         u'紅樓夢':{
             'author':u'曹雪芹',
             'price':113
         },
         u'水滸傳':{
             'author':u'施耐庵',
             'price':135
         }
     }
  2. 再在主程序中將其傳給前端 html 文件

    return render_template('index.html',books = books)
  3. 最後在前端模板中調用

    <table>
             <tbody>
                 <tr>
                     <th>書名</th>
                     <th>做者</th>
                     <th>價格</th>
                 </tr>
                 {% for k,v in books.items() %}
                 <tr>
                     <td>{{ k }}</td>
                     <td>{{ v.author }}</td>
                     <td>{{ v.price }}</td>
                 </tr>
                 {% endfor %}
             </tbody>
         </table>

4. 過濾器

過濾器能夠理解爲 Linux 中的管道符 |,將獲取的變量通過管道符後篩選出想要的內容。在 flask 中有不少過濾器,這裏介紹 2 個比較經常使用的過濾器:defaultlength。要注意的是,過濾器只能針對變量{{ params }}使用。

4.1 default

default 過濾器的做用:若是當前變量不存在,可使用指定的默認值

對於 default 過濾器,咱們作一個實驗:若是用戶有頭像則顯示本身的頭像,若是用戶沒有頭像則顯示默認頭像。

在 html 文件中使用以下所示:

<img src="{{ avatar | default('https://i.imgur.com/ROhBvig.png') }}">

該行代碼表示:
    若是後端主程序有傳遞 avatar 變量過來,那麼就使用 avatar 變量的值;
    若是後端主程序沒有傳遞 avatar 變量過來,那麼就使用 default 過濾器指定的內容

對於本例而言,default 後面跟的圖片地址應該是默認頭像的地址,avatar 變量內保存的值應該是用戶頭像

4.2 length

能夠統計有長度屬性的變量的長度。語法與 default 過濾器同樣,但不用在後面跟上指定的變量。對於 length 過濾器,咱們作一個實驗:統計評論的數量並顯示評論內容。

  1. 主程序代碼:

    comments = [
         {
             'user':u'螞蟻有毒',
             'content':u'我不喜歡這個東西'
         },
         {
             'user':u'楊烺',
             'content':u'有同感,我也是'
         }
     ]
    
     return render_template('index.html',comments = comments)
  2. html 模板代碼:

    <p>
         評論數:({{ comments | length }})
     </p>
     <hr>
     {% for comment in comments %}
         <li>
             {{ comment.user }}:
         </li>
         <ol>
             {{ comment.content }}
         </ol>
     {% endfor %}
  3. 此外還有其餘不少過濾器,能夠自行查找資料。

5. 繼承和使用 block

5.1 繼承

繼承的概念和麪向對象編程的類的繼承是同樣的,只不過在這裏繼承的對象是模板。能夠建立一個經常使用模板,而且定義相關接口,能夠供其餘模板所使用。繼承的做用是:能夠把一些公共的代碼放在父模板中,避免編寫重複的代碼。

當建立好了一個模板後,在子模板中可使用 {% extends 'base.html' %} 來繼承該模板

5.2 使用 block

可是若是我要在子模板中編寫本身所特有的內容,應該怎麼辦?這時候就須要在父模板中寫一個接口,來讓子模板實現:

  1. 在父模板須要的地方定義接口的方式是:{% block abc %}{% endblock %}
  2. 在子模板中一樣須要寫上 {% block abc %} code {% endblock %},而且在 code 處寫子模板須要的代碼。
  3. 須要注意的是,子模板必須在父模板定義的接口中寫代碼,否則沒有做用。以下所示:
1. 父模板(Base.html):

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        {% block title %}
            <title>Base</title>
        {% endblock %}
        <style>
            .nav{
                background: #3a3a3a;
                height: 65px;
            }
            ul{
                overflow: hidden;
            }
            ul li{
                float: left;
                list-style: none;
                padding: 0 10px;
                line-height: 65px;
            }
            ul li a{
                color: #fff;
            }
        </style>
    </head>
    <body>
    
        <div class="nav">
            <ul>
                <li><a href="#">首頁</a></li>
                <li><a href="#">發佈問答</a></li>
            </ul>
        </div>
    
    {% block content %}{% endblock %}
    
    </body>
    </html>

2. 子模板(index.html):

    {% extends 'base.html' %}
    
    {% block title %}
        <title>首頁</title>
    {% endblock %}
    
    {% block content %}
        <h1>螞蟻有毒的首頁</h1>
    {% endblock %}

6. URL 連接和加載靜態文件

6.1 URL 連接

咱們若是想要實現:點擊一個按鈕就能跳轉到另外一個頁面上。那麼咱們就要使用連接跳轉技術,在 flask 中,能夠經過 <a href={{ url_for('視圖函數名') }}>哈哈</a> 來實現

6.2 加載靜態文件

css,js,img 這 3 個文件都屬於靜態文件,都要放在項目的 static 文件夾下。若是咱們要對模板中的一些內容進行渲染,如:對 <a> 標籤的內容進行渲染,那麼咱們能夠將本來寫在 <head> 標籤中的 <style> 標籤的內容放到 css 文件中,再在模板中使用 url_for()css 文件的渲染方式連接進來。步驟以下:

1. 加載 css 文件<link>標籤

  1. static 文件夾下建立一個新的文件夾 css,再在 css 文件夾下建立一個 css 文件,可隨意命名(好記就行),如:head.css
  2. 在使用了 <a></a> 標籤的模板中,若想引用 css 文件的內容,能夠用 url_for() 連接進來,但格式與連接網址略有不一樣。正確連接靜態文件的方式爲:

    在模板 <head> 標籤範文內:
     <link rel=''stylesheet href='{{ url_for('static',filename='css/head.css') }}'>
     注意!文件路徑

2. 加載 img 文件<img>標籤

  1. 第一步和加載 css 文件的第一步同樣,建立文件夾和文件:/static/img/index.jpg
  2. 第二步在模板中須要插入圖片的地方使用以下代碼:

    <img src='{{ url_for('static',filename='img/index.jpg') }}' alt=''>

3. 加載 js 文件<script>標籤

  1. 第一步和加載 css 文件的第一步同樣,建立文件夾和文件:static/js/index.js
  2. 第二步在模板中須要插入腳本的地方使用以下代碼:

    <script src='{{ url_for('static',filename='js/index.js') }}'>

通常靜態文件也就這三樣,使用方法如上所示。


3、數據庫

1. 背景知識和軟件安裝

  1. 安裝 MySQL

    數據庫咱們使用的是 MySQL,下載地址:https://dev.mysql.com/downloads/mysql/,下載社區版就好了。注意要下載安裝包(.msi)而不是壓縮包(.zip)

    若是在安裝過程當中提示咱們要安裝 windows 插件,那就按照它提示的網址去下載,沒有給網址的話就百度谷歌吧。

  2. 安裝 MySQL-python

    MySQL-python 是一個驅動或者說是一個插件,若是咱們想要經過 python 去操做 MySQL 就須要藉助這個軟件去作。在 Windows 下安裝 MySQL-python 的步驟以下:
    1. 進入 python 的 flask 虛擬環境並啓動:

      cd C:\Virtualenv\flask-env\Script\
       active
       pip install mysql-python
    2. 安裝的時候會報錯,咱們須要去 www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python 這個網站上下載一個非官方的插件MySQL_python-1.2.5-cp27-none-win_amd64.whl,來使 mysql-python 支持 Windows 。能夠下載到 Virtualenv 所在盤的任意目錄。
    3. 安裝這個插件:進入到該文件所在目錄執行命令 pip install MySQL_python-1.2.5-cp27-none-win_amd64.whl 便可。

  3. 安裝 Flask-SQLALchemy

    1. 介紹
      Flask-SQLALchemy 是一套 ORM(Object Relationship Mapping,模型關係映射) 框架。

      好處:可讓咱們操做數據庫就和操做對象同樣簡單,很是方便。由於一個表就抽象成一個類,一條數據就抽象成該類的一個對象。這樣一來,咱們去操做數據庫的時候,就不用去寫什麼 select 之類的數據庫語句,而是能夠直接用 python 操做實例對象就好了。
    2. 安裝

      cd C:\Virtualenv\flask-env\Script\
       active
       pip install flask-sqlalchemy

2. SQLAlchemy 鏈接數據庫

  1. 先建立一個數據庫:

    進入 MySQL 的命令行 -> 輸入密碼 -> create database [db_demo1(數據庫名)] charset utf8;

  2. 在主 app 程序中從 flask_sqlalchemy 導入 SQLAlchemy 類並初始化

    from flask_sqlalchemy import SQLAlchemy
     app = Flask(__name__)
     db = SQLAlchemy(app)
  3. config.py 文件中配置數據庫信息並鏈接

    DIALECT = 'mysql'
     DRIVER = 'mysqldb'
     USERNAME = 'root'
     PASSWORD = 'root'
     HOST = '127.0.0.1'
     PORT = '3306'
     DATABASE = 'db_demo1'
    
     SQLALCHEMY_DATABASE_URI = "{}+{}://{}:{}@{}:{}/{}?charset=utf8".format(DIALECT, DRIVER, USERNAME, PASSWORD, HOST, PORT, DATABASE)
  4. 在主 app 文件中,添加配置文件

    import config
    app.config.from_objece(config)

  5. 作測試,看有沒有出現問題

    db.create_all()

3. SQLAlchemy 模型與表映射

對於一個表,咱們能夠建立一個類(模型)來與之對應(一張表對應一個類),以下表:

  1. 先定義好表格是怎麼樣的

    articel 表:
     create table article(
         id int primary key autoincrement,
         title varchar(100) not null,
         content text not null
     )
  2. 再建立出對應的(模型)類

    class Article(db.Model):    # 必定要繼承自 db.Model
         __tablename__ = 'article'   # 指定表名,默認爲類的小寫字符
         id = db.Column(db.Integer,primary_key=True,autuincrement=True)      # 表中的每一個字段都要用 Column 建立,而後指定數據類型主鍵自增加等屬性
         title = db.Column(db.String(100),nullable=False)
         content = db.Column(db.Text,nullable=False)
  3. 將全部建立的模型(類)都映射到數據庫中成爲一個個的表格

    db.create_all()

鏈接數據庫的總結:

  1. 模型(類)需繼承自 db.Medel,表格中的字段必需要用 db.Column 映射
  2. 數據類型:

    db.Integer          表明      int,
     db.String(length)   表明      varchar,須要指定長度
     db.Text             表明      text
  1. 其餘屬性:

    primary_key=True    表明      主鍵
     nullabel=True       表明      可爲空,默承認以
     autoincrement=True  表明      主鍵自增加
  2. 將全部的模型(類)都在數據庫中建立:

    db.create_all()

4. 數據庫的增刪改查

對數據庫的增刪改查,通常都在視圖函數裏面寫,這樣在瀏覽器上對某個 URL 進行訪問時,就會執行對應的視圖函數裏面的代碼,從而達到操做數據庫的目的。

flask 對數據庫的增刪改查,包括提交,都使用的是 db.session 操做。

  1. 增長

    1. 建立一個表中的數據,即實例化模型(類):article1 = Article(title='Monday',content='something')
    2. 將 add 操做添加到事務中:db.session.add(article1)
    3. 更新到數據庫中:db.session.commit()
  2. 刪除

    1. 把要刪除的數據查找出來:article1 = Article.query.filter(Article.title=='Monday').firsr()
    2. 把找出來的數據刪除:db.session.delete(article1)
    3. 作事務的提交:db.session.commit()
  3. 修改

    1. 把要修改的數據查找出來:article1 = Article.query.filter(Article.title=='Monday').firsr()
    2. 把找出來的數據作修改:article1.title = 'Sunday'
    3. 作事務的提交:db.session.commit()
  4. 查詢

    result = Article.query.filter(Article.title=='Monday')      # 查找是針對模型(類)的,使用 query 屬性,該屬性繼承自 db.Model,並用 filter() 來過濾條件
     article1 = result.first()       # 實際上篩選出來的對象是放在一個 list 中,可用 .first() 取 list 中的第一個值
     print 'title:%s' % article.title
     print 'content:%s' % article.content

5. SQLAlchemy 的外鍵約束

建立 2 張表:userarticle,其中 articleauthor_id 引用是 userid,即 2 者是外鍵關係。

  1. 數據庫語句建立:

    建立用戶表:
     create table users(
         id int primary key autoincrement
         username varchar(20) not null
     )
    
     建立文章表:
     create table article(
         id int primary key autoincrement
         title varchar(100) not null
         content text not null
         author_id int,
         foreign key `author_id` reference `users.id`    # 用外鍵相關聯
     )
  2. flask-sqlalchemy 模型建立:

    建立用戶表:
     class Users(db.Model):
         __tablename__ = 'users'
         id = db.Column(db.Integer,primary_key=True,autoincrement=True)
         usernmae = db.Column(db.String(20),nullable=False)
    
     建立文章表:
     class Article(db.Model):
         __tablename__ = 'article'
         id = db.Column(db.Integer,primary_key=True,autoincrement=True)
         title = db.Column(db.String(100),nullabel=False)
         content = ab.Column(db.Text,nullabel=False)
         author_id = db.Column(db.Integer,db.ForeignKey('users.id'))     # 外鍵關係
  3. 實例化模型(類)

    由於 article 依賴與 user.id 存在,因此先建立 user,在建立 article。

    user1 = User(username='user1')
     db.session.add(user1)
     db.session.commit()
    
     article = Article(title='aaa',content='bbb',author_id=1)
     db.session.add(article)
     db.session.commit()
  4. 需求:查詢 title ='aaa' 的文章的做者

    4.1 傳統方法

    article = Article.query.filter(Article.title=='aaa').first()    # 找出標題爲 aaa 的文章
     author = article.author_id      # 找出文章的做者
    
     user = User.query.filter(User.id==author).fitst()       # 找出 id 爲做者 id 的做者
    
     print 'username:%s' % user.id       # 打印做者名

    固然了,這個方法實在是太過於繁瑣,做爲一個優秀的框架模型,SQLAlchemy 是能夠用更先進的方法去找的,以下所示:

    4.2 用 SQLAlchemy 語法

    article = Article.query.filter(Article.title=='aaa')
     article.author.username     # 找出標題爲 aaa 的文章做者
    
     user = User.query.filter(User.username=='user1')
     user1.articles      # 找出做者 user1 寫過的全部文章

    固然了,這個只是理想中的方法,這樣的方式更方便咱們去獲取需求,不過 SQLAlchemy 已經實現了這種方法,只須要作以下映射便可使用:

    # 先在 Article 模型中添加屬性
     author = db.relationship('User',backref=db.backref('articles'))
    
     # 再查找某篇文章的做者
     article = Article.query.filter(Article.title=='aaa').first()
     print 'username : %s' % article.author.username
    
     # 查找某個做者的文章
     user = User.query.filter(User.id=='1').first()
     print u'%s 的文章:'% user.username
     for article in user.articles:
         print article.title
  5. 當對兩個 table 的關係用了 relationship 關聯外鍵以後,那麼 articleauthor 就沒必要在實例化的時候指定了,因此當你添加一篇文章,能夠進行以下操做:

    article1 = Article(title='111',content='222')
     article1.author = User.query.filter(User.username=='myyd').first().id

6. flask_script 命令行操做

簡介:Flask_Script 可讓程序經過命令行的形式來操做 Flask,例如:經過命令跑一個開發版本的服務器、設置數據庫,定時任務等等。要使用 Flask_Script,能夠在 Flask 虛擬環境中經過 pip install flask-script 安裝最新版本。

6.1 編寫 flask_script 腳本代碼

  1. 新建一個 manage.py 文件,將代碼寫在該文件中,而不是寫在主 app 文件中。內容以下:

    # encoding:utf-8
    
     from flask_script import Manager        # 從 flask_script 導入 Manager 
     from flask_script_demo1 import app      # 從 flask_script_demo1 導入 app 
    
     manage = Manager(app)       # 初始化 app 
    
     @manage.command     # 裝飾器
     def runserver():        # 執行命令的程序寫在這個函數下
         print u'服務器跑起來了。'
    
     @manage.command     # 裝飾器
     def stopserver():       # 執行命令的程序寫在這個函數下
         print u'服務器關閉了。'
    
     if __name__ == '__main__':
         manage.run()
  2. 命令行調用 manage.py 文件:

    在虛擬環境的命令行下,用 python manage.py command 執行 manage.py 文件下的某個程序,如:python manage.py runserverpython manage.py stopserver 分別會執行 manage.py 文件中的 runserver()stopserver() 方法。

6.2 從其餘命令文件中調用命令

  1. 若是有一些關於數據庫的操做,咱們能夠放在一個文件中執行。如 db_script.py 文件:

    # encoding: utf-8
    
     from flask_script import Manager
    
     # 由於本文件不是做爲主 app 文件,因此不須要寫 if __name__ == '__main__'
     # 也不須要在初始化的時候傳入 app 文件
    
     DBManage = Manager()
    
     @DBManage.command
     def init():
         print u'服務器初始化完成。'
    
     @DBManage.command
     def migrate():
         print u'數據庫遷移完成。'

    這時候要想用上 db_script.py 裏定義的命令,須要在主 manage.py 文件中導入該文件並引用該文件的命令:

    from db_scripts import DBManage     # 導入 db_script.py 文件
     manage.add_command('db',DBManage)   # 引用該文件
    
     # 以後要是想使用 db_script.py 中的命令,命令行中就要經過 python manage.py db init 來調用
     # 其中,db 是 manage.add_command() 中引號內的值,調用子命令的方法就是這種格式
  2. 總結:

    若是直接在主 manage.py 文件中寫命令,調用時只須要執行 python manage.py command_name 便可

    若是將一些命令集中在另外一個文件中,那麼就須要輸入一個父命令,好比 python manage.py db init

  3. 例子:兩個文件的完整代碼以下

    1. manage.py
    
     # encoding:utf-8
    
     from flask_script import Manager
     from flask_script_demo1 import app
     from db_scripts import DBManage
     manage = Manager(app)
    
     @manage.command
     def runserver():
         print u'服務器跑起來了。'
    
     @manage.command
     def stopserver():
         print u'服務器中止了。'
    
     manage.add_command('db',DBManage)
    
     if __name__ == '__main__':
         manage.run()
    
    
     2. db_script.py
    
     # encoding: utf-8
     from flask_script import Manager
    
     DBManage = Manager()
    
     @DBManage.command
     def init():
         print u'服務器初始化完成。'
    
     @DBManage.command
     def migrate():
         print u'數據庫遷移完成。'

7. 分開 Models 和解決循環引用

以前咱們都是將數據庫的模型(類)放在主 app 文件中,可是隨着項目愈來愈大,若是對於加入的新的模型,咱們還放在主 app 文件中,就會使主 app 文件愈來愈大,同時也愈來愈很差管理。因此咱們能夠新建一個專門存放模型的文件。如 models.py 文件用來專門存放模型的文件。

  1. 將本來在主 app 文件中定義的模型(類)移動到 models.py 文件中,可是會提示錯誤,因此咱們在 models.py 文件中導入主 app 文件的 db:from models_sep.py import db,兩個文件的完整代碼下所示:

    1. # models_sep.py 文件
    
     from flask import Flask
     from flask_sqlalchemy import SQLAlchemy
     from models import Article
    
     app = Flask(__name__)
     db = SQLAlchemy(app)
     db.create_all()
    
     @app.route('/')
     def index():
         return 'index!'
    
     if __name__ == '__main__':
         app.run()
    
     2. # models.py 文件
    
     from flask_script_demo1 import db
    
     class Article(db.Model):
         __tablename__ = 'articles'
         id = db.Column(db.Integer,primary_key=True,autoincrement=True)
         title = db.Column(db.String(100),nullable=False)
         content = db.Column(db.Text,nullable=False)
  2. 執行以上文件,會報錯。

    報錯提示:ImportError: cannot import name Article,出現此類報錯,先排查路徑和導入的內容是否有錯,若保證沒錯,則極可能是出現循環引用。

    報錯緣由:循環引用,即 models_sep.py 引用了 models.py 中的 Article,而 models.py 又引用了 models_sep.py 中的 db,從而形成循環引用。

  3. 解決循環引用:

    解決方法:將 db 放在另外一個文件 exts.py 中,而後 models_sep.pymodels.py 都從 exts.py 中引用 db 變量,這樣就能夠打破引用的循環。

    三個文件的代碼以下:

    1. # exts.py 文件
    
     from flask_sqlalchemy import SQLAlchemy
     db = SQLAlchemy()
    
     2. # models_sep.py 文件
    
     from flask import Flask
     from models import Article
     from exts import db
     import config
    
     app = Flask(__name__)
     app.config.from_object(config)
     db.init_app(app)
    
     @app.route('/')
     def index():
         return 'index'
    
     if __name__ == '__main__':
         app.run()
    
     3. # models.py 文件
    
     from exts import db
    
     class Article(db.Model):
         __tablename__ = 'articles'
         id = db.Column(db.Integer,primary_key=True,autoincre)
         title = db.Column(db.String(100),nullable=False)
         content = db.Column(db.Text,nullable=False)
  4. 總結:

    分開 models 的目的是:讓代碼更方便管理。

    解決循環引用的方法:把 db 放在一個單獨文件中如 exts.py ,讓主 app 文件models 文件都從 exts.py 中引用。

8. flask-migrate 數據庫遷移

在以上基礎上,咱們將模型(類)映射到數據庫中,調用 db.create_all() 進行映射。而後運行,發現報錯:No application found. Either work inside a view function or push an application context.,是由於在 db 的上下文中,沒有將 app 推入到棧中。

8.1 所謂上下文(flask獨有的特性):

當用戶訪問了服務器,則服務器會將當前 app 並加載到 app 棧中;若是用戶沒有訪問服務器,那麼即便 app 已經建立,可是沒有被服務器加載到 app 棧中。若這時候運行服務器,則 app 棧中沒有加載 app,當 db.init_app(app) 去 app 棧中取 app 對象的時候沒有成功獲取,因此報錯。

  1. 緣由:

    狀況一:
    1. db = SQLAlchemy(app) 這句代碼運行的時候,會將 app 推到app棧中;
    2. 而後 db.create_all() 就能夠經過 app 棧獲取到棧頂元素 app 了。
    狀況二:
    1. 使用分開 models 後,db = SQLAlchemy() 時沒有傳入 app,因此沒有將 app 推到棧中;
    2. 那麼在主 app 文件中經過 db.init_app(app) 將 app 與 db 綁定時,會去 app 棧中取 app,可是沒有獲取到,因此報錯。
  2. 解決方法:將 app 手動推到 app 棧中:

    # 將 db.create_all() 替換爲如下代碼
     with app.app_context():
         db.create_all()

8.2 migrate 數據庫遷移:

這個時候若是咱們的模型(類)要根據需求添加一個做者字段,這時候咱們須要去修改模型 Article,修改完成咱們須要再映射一遍。可是對於 flask-sqlalchemy 而言,當數據庫中存在了某個模型(類)後,再次映射不會修改該模型的字段,即再次映射不會奏效。

傳統解決辦法:

在數據庫中刪除該模型對應的表格,再將帶有新字段的模型從新進行映射。

很顯然,這種方式明顯很簡單粗暴,很是不安全,由於在企業中一個數據庫中的表格是含有大量數據的,若是刪除可能會形成重大損失。因此咱們須要一個能夠動態修改模型字段的方法,使用 flask-migrate。先安裝:在虛擬環境下使用命令 pip install flask-migrate 便可。

8.3 使用 flask-migrate 動態修改模型字段

使用 flask-migrate 的最簡單方法是:藉助 flask-script 使用命令行來對 flask-migrate 進行操做。一共有好幾個步驟,分別說明一下:

  1. 新建文件 manage.py

    新建 manage.py 文件後:

    1. 導入相應的包並初始化 manager

      from flask_script import Manager
       from migrate_demo import app
       from flask_migrate import Migrate,MigrateCommand
       from exts import db
       manager = Manager(app)
    2. 要使用 flask_migrate 必須綁定 appdb

      migrate = Migrate(app,db)
    3. MigrateCommand 命令添加到 manager

      manager.add_command('db',MigrateCommand)
    4. manage.py 文件代碼以下:

      from flask_script import Manager
       from migrate_demo import app
       from flask_migrate import Migrate,MigrateCommand
       from exts import db
      
       manager = Manager(app)
      
       # 1. 要使用 flask_migrate 必須綁定 app 和 db
       migrate = Migrate(app,db)
      
       # 2. 把 MigrateCommand 命令添加到 manager 中
       manager.add_command('db',MigrateCommand)
      
       if __name__ == '__main__':
           manager.run()
  2. 主 app 文件不須要再對模型進行映射,因此能夠將如下語句給刪除:

    with app.app_context():
         db.create_all()         # 將模型映射到數據庫中
  3. manage.py 文件中,導入須要映射的模型(類):

    由於在主 app 文件中已經再也不須要對模型進行映射,而對模型的操做是在 manage.py 文件中進行的,包括 flask-migrate 動態映射,因此要導入須要映射的模型。

    from models import Article
  4. 完成以上步驟後,便可到命令行中更新數據庫中的模型了:

    1. python manage.py db init,初始化 flask-migrate 環境,僅在第一次執行的時候使用。
    2. python manage.py db migrate,生成遷移文件
    3. python manage.py db upgrade,將遷移文件映射到數據庫的表格中

背景知識:HTTP 協議是無狀態的,每次鏈接斷開後再從新鏈接,服務器是不記得瀏覽器的歷史狀態的。也就是說:即便第一次瀏覽器訪問服務器時登錄成功後,只要斷開與服務器的鏈接,服務器就會忘記瀏覽器的信息,那麼當瀏覽器與服務器再次成功鏈接時,服務器不知道瀏覽器的相關信息。

  1. cookie

    而 cookie 的出現就是爲了解決這個問題:當瀏覽器第一次在服務器上登陸成功時,服務器會將一些數據(cookie)返回給瀏覽器,而後瀏覽器將 cookie 保存在本地,第二次訪問服務器時瀏覽器會自動地將上次存儲的 cookie 發送給服務器,服務器經過 cookie 判斷瀏覽器的信息從而提供更好的服務(如自動記錄用戶上次瀏覽的位置等)。

    服務器和瀏覽器交互 cookie 的過程對用戶而言是透明的。

    cookie 信息保存在用戶的瀏覽器中。

  2. session

    session 的做用和 cookie 相似,都是爲了保存用戶的相關信息。不一樣的是:cookie 是將用戶的信息保存在用戶的瀏覽器本地,而 session 是將用戶的信息保存在服務器上。相對而言,session 比 cookie 更加安全和穩定,可是會佔用服務器的一些資源,不過影響不大。同時,session 還支持設置過時時間,也從另外一方面保證了用戶帳號的安全。

9.2 flask 中 session 的工做機制

flaks 中 session 的工做機制是:服務器將用戶的敏感信息加密後存入 session 中,而後把 session 放在 cookie 中,再將 cookie 返回給瀏覽器,下次瀏覽器訪問的時候將 cookie 發送給服務器,服務器再根據瀏覽器發送的 cookie 取出裏面的 session,再從 session 中取出用戶的信息。

這樣作能夠節省服務器的一些資源。在安全問題上,由於用戶的信息先通過加密再存入 session 中的,因此仍是有必定的安全性能的。

9.3 操做 session

想要在 flask 中操做 session,咱們必須先導入 session 模塊:from flask import session

當用戶訪問某一個 URL 的時候,相應的視圖函數就會有相應的動做,咱們能夠在視圖函數中進行 session 的操做。實際上操做 session 的方法和操做字典是同樣的。

不過對 session 進行操做以前,要先設置一個 SECRET_KEY,這個 SECRET_KEY 就是用來對用戶的信息進行加密的密鑰,加密後的數據再保存到 session 中。設置 SECRET_KEY 的方法有兩種:

  1. 新建一個 config 文件,在 config 文件中添加 SECRET_KEY = 'xxx' 這條語句,再在主 app 文件中使用 config 文件的配置 app.config.from_object(config) 就好了。
  2. 直接在主 app 文件中:app.config['SECRET_KEY'] = 'xxx'

但實際上,SECRET_KEY 要求用 24 個字符來賦值,咱們可使用 os 模塊,即 import os

  1. session 添加數據:

    session['username'] = 'myyd'
  2. session 獲取數據:

    有 2 種方式,第一種和操做字典的方法同樣:return session['username];第二種是用session 中的 get() 方法:session.get('username)

    推薦使用第二種方式:session.get('username'),由於當 username 不存在時,第一種方法會拋出異常,而第二種方法是返回 None。

  3. session 刪除數據:

    session.pop('username')

    對於 session 的數據刪除操做,咱們能夠設計一個小實驗,即在刪除先後分別打印 session 中的數據,看看結果如何。

  4. session 清除全部數據:

    session.clear()

    一樣能夠設計一個和刪除數據同樣的小實驗,來對清除操做進行驗證。

  5. 源代碼以下:

    # encoding:utf-8
    
     from flask import Flask,session
     import os   # 該模塊用於生成隨機字符串
    
     app = Flask(__name__)
     app.config['SECRET_KEY'] = os.urandom(24)   # SECRET_KEY 爲隨機生成的字符串
    
     @app.route('/')
     def index():
         session['username'] = 'myyd'        # 添加 session 數據
         return 'index'
    
     @app.route('/get/')
     def get():
         username2 = session.get('username')     # 獲取 session 數據
         return username2
    
     @app.route('/delete/')
     def delete():
         print session.get('username')
         session.pop('username')             # 刪除 session 中指定的數據
         print session.get('username')
         return 'success.'
     @app.route('/clear/')
     def clear():
         print session.get('username')
         session.clear()                     # 清除 session 中全部的數據
         print session.get('username')
         return 'success.'
    
     if __name__ == '__main__':
         app.run(debug=True)

    以上是對 session 的一些基本操做。

同時還須要注意一點的是:

若服務器重啓後,且瀏覽器沒有從新獲取 session 時,咱們直接去獲取瀏覽器的 session,會報錯!
ValueError: View function did not return a response,提示視圖函數沒有返回值。

這是由於:python 是腳本語言,運行時從頭開始解釋,則當服務器重啓後,SECRET_KEY 會被重置,服務器想要讀取瀏覽器發送過來的 session 中的數據時,會使用新的 SECRET_KEY 進行解密,而新的 SECRET_KEY 並不可以對 session 中的數據進行解密,因此服務器獲取失敗,不能返回值。

10. GET 、POST 和 鉤子函數

10.1 概述

GET 和 POST 的區別:

  1. 使用場景

    GET 請求:單純從服務器獲取資源,沒有對服務器產生影響,即沒有對服務器的數據進行修改。

    POST 請求:從服務器獲取資源,而且傳給服務器一些須要處理的數據。服務器對瀏覽器傳過來的數據進行相關邏輯判斷或者處理後返回給瀏覽器。此時是對服務器產生了某些影響的,而不是單純的訪問瀏覽器某些資源。

  2. 傳參

    GET 請求:請求的參數跟在 URL 後面並用 ? 號鏈接。

    POST 請求:請求的參數不是放在 URL 後,而是將參數放在 Form 表單中。

10.2 使用 GET

  1. 經過一個小例子來幫助理解:

    index.html 首頁設置一個 <a> 標籤,跳轉至 search 的頁面,同時在該 <a> 標籤中攜帶參數 q=hello,在服務器端獲取瀏覽器請求的參數並返回給瀏覽器。

    先建立一個首頁 index.html 並在主 app 程序中用 render_template 返回首頁:

    from flask import render_template   # 導入該特性
    
     return render_template('index.html')    # 在視圖函數 index 中返回首頁

    同時在首頁用 <a> 標籤跳轉到 search 頁面,並攜帶參數傳給服務器的 search() 視圖函數。

    <a href="{{ url_for('search',q=hello) }}">點擊查詢</a>

    而後在主 app 程序的 search 視圖函數中,用 request 對象獲取用戶提交的值,不過要先從 flask 中導入該特性

    from flask import request   # 導入
    
     return 'the parameter you have submit is:%s' % request.args.get('q')    # 獲取並返回
  2. 注意這個 request 對象:

    request 能夠獲取用戶提交的請求參數,包括 GET 和 POST 提交的參數信息。reques 對象實際上一個字典,其獲取的數據是以字典的格式保存下來的。以下所示:

    # 若是提交的是這樣的數據
     request.args = {
         'q':'hello'
         'a':'world'
     }
     # 那麼打印出來是下面這個樣子的
     print requesr.args
     ImmutableMultiDict([('q', u'hello'), ('a', u'world')])

    因此咱們能夠向字典同樣經過 request.args.get('q') 來獲取用戶提交的參數。

  3. 參考代碼以下:

    # 主 app 文件
     from flask import Flask,request,render_template
    
     @app.route('/')
     def index():
         return render_template('index.html')
    
     @app.route('/search/')
     def search():
         return u'你要查詢的參數是:%s' % request.args.get('q')
    
     # index.html 文件
    
     <a href="{{ url_for('search',q='hello') }}">點擊查詢</a>

10.3 使用 POST

POST 請求能夠藉助模擬登陸的過程來作一個小實驗:在 login.html 中建立一個表單,該表單要指定後臺處理的視圖函數,同時還須要指定提交請求的方式爲 POST。還要在主 app 程序的視圖函數中顯式地標註該視圖函數支持 POST 和 GET。

  1. login.html 中建立表單:

    建立代碼略,後面一塊兒給出。

    若是僅僅作了 render_template 的映射和 url_for 的反轉,還不夠,由於視圖函數 login() 還未支持 POST 請求方式,而默認的視圖函數只能支持 GET 請求。因此還須要進行以下配置:

    @app.route('/login/',method=['GET','POST'])

    須要注意的是:

    1. 不能只寫 POST,由於只寫 POST 則該視圖函數只支持 POST;
    2. 而咱們去請求 login 網頁的時候用的是 GET,向 login 提交數據的時候才用 POST,因此 login() 必須同時支持兩種方法
  2. 咱們能夠在 login() 這個視圖函數中根據用戶的請求方式來判斷用戶是否登陸,從而返回不一樣的內容。

    由於用戶請求 login 這個頁面的時候是用 GET,而在 login 頁面中進行登陸所提交的請求是 POST。獲取用戶請求提交的方法,也是用 request 對象:request.methods

    這裏要注意,咱們獲取 POST 請求的參數時,雖然也是用 request 對象,可是這裏應該用 request.form.get('username'),並且在 login.html 的表單中,須要爲 <input> 標籤指定名字這樣才能夠在後臺經過 name 獲取到內容。因此 login() 視圖函數應該這麼寫:

    @app.route('/login/,methods=['GET','POST'])
     def login():
         if request.method == 'GET':
             return render_template('login.html')
         else:
             return u'你好,%s' % request.form.get('username')
  3. 源代碼:

    1. 主 app 文件:
    
     # encoding:utf-8
     from flask import Flask,render_template,request
     app = Flask(__name__)
    
     @app.route('/')
     def index():
         return render_template('index.html')
    
     @app.route('/search/')
     def search():
         return u'你要查詢的參數是:%s' % request.args.get('q')
    
     @app.route('/login/',methods=['GET','POST'])
     def login():
         if request.method == 'GET':
             return render_template('login.html')
         else:
             return u'你好,%s' % request.form.get('username')
    
     if __name__ == '__main__':
         app.run(debug=True)
    
     2. index.html 文件
    
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>index</title>
     </head>
     <body>
         <a href="{{ url_for('search',q='hello') }}">點擊查詢</a>
         <a href="{{ url_for('login') }}">點擊登陸</a>
     </body>
     </html>
    
     3. login.html 文件
    
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>login</title>
     </head>
     <body>
     <form action="{{ url_for('login') }}" method="post">
         <table>
             <tbody>
                 <tr>
                     <td>用戶名:</td>
                     <td><input type="text" placeholder="請輸入用戶名" name="username"></td>
                 </tr>
                 <tr>
                     <td>密碼:</td>
                     <td><input type="password" placeholder="請輸入密碼" name="password"></td>
                 </tr>
                 <tr>
                     <td></td>
                     <td><input type="submit" value="登陸"></td>
                 </tr>
             </tbody>
         </table>
     </form>
     </body>
     </html>

10.4 總結 GET 和 POST

  1. 獲取一切和請求相關的信息,使用 request 對象。如:

    獲取請求方法:request.method
     獲取GET的參數:request.args
     獲取POST的參數:request.form
  2. 對於須要支持 POST 請求的視圖函數,要在其裝飾器上顯式指定:

    @app.route('/login/',methods=['GET','POST'])

    而且要在模板裏 <input> 標籤中,用 name 來標識 valuekey,方便後臺獲取。同時在寫表單 form 的時候要指定 action 爲用來處理的視圖函數名。

10.5 鉤子函數

鉤子函數能夠插入到執行過程之間,即在正常的程序執行過程當中插入鉤子函數的執行。舉歌例子,原來是執行了 A 函數後就接着執行 B 函數,即執行過程爲 A->B;可是因爲一個鉤子函數 C 的介入,那麼執行過程改變爲 A->C->B。這裏介紹 2 個鉤子函數 before_requestcontext_processor

10.5.1 before_request

before_request 顧名思義,就是在 request 執行以前執行,實際上就是在視圖函數執行以前執行的。不過 before_requestroute 同樣,只是一個裝飾器,它的做用就是把須要的在視圖函數執行以前執行的動做放入一個函數並對該函數實現'鉤子'的功能。

在執行每一個視圖函數以前都先執行 before_request 定義的函數。

  1. 經過一個小實驗,就能知道 before_request 的功能了:

    from flask import Flask
    
     app = Flask(__name__)
    
     @app.route('/')
     def index():
         print 'index'
         return 'index'
    
     @app.before_request
     def My_before_request():
         print 'hello,world'
    
     if __name__ == '__main__':
         app.run(debug=True)

    能夠在 pycharm 的控制檯中看到:先打印了 hello,world,纔打印的 index;說明了 My_before_request() 是在 index 以前執行的。

  2. 這邊擴展一下小案例。

    需求:網站中有一個look頁面須要登陸以後才能訪問,利用 before_request 來實現對用戶判斷用戶是否登陸。

    思路:

    1. 定義一個首頁 index.html,一個登陸頁面 login.html 和一個查看頁面 look 以及他們對應的視圖函數。

    2. index 視圖函數中返回 index.html 頁面,並在 index.html 中定義兩個 <a> 標籤用以分別跳轉至 login.htmllook.html

      <a href="{{ url_for('login') }}">點擊登陸</a>
       <a href="{{ url_for('look') }}">點擊查看</a>
    3. login 視圖函數中對請求的方法作判斷:

      GET 方法表明用戶須要獲取到這個頁面,則返回 login.html 頁面讓用戶登陸;

      POST 方法表明用戶提交了登陸的信息,則判斷用戶是否合法;若合法則發送 session,若非法則提示非法。

      @app.route('/login/',methods=['GET','POST'])
       def login():
           if request.method == 'GET':
               return render_template('login.html')
           else:
               username = request.form.get('username')
               password = request.form.get('password')
               if username == 'MYYD' and password == '123456':
                   session['username'] = username
                   return u'登陸成功!'
               else:
                   return u'用戶名或密碼錯誤!'
    4. before_request 中定義 My_before_request,而後取出 session 的信息放入對象 g 中。

      @app.before_request
       def My_before_request():
           if session.get('username'):
               g.username = session.get('username')
    5. look 是視圖函數中,判斷根據對象 g 提供的信息判斷該用戶是否已經登陸,若登陸則返回 look.html 頁面;若沒登陸則重定向至 login.html 頁面。

      @app.route('/look/')
       def look():
           if hasattr(g,'username'):
               return u'修改爲功!'
           else:
               return redirect(url_for('login'))
  3. 完整代碼以下:

    # hook.py
    
     from flask import Flask,render_template,request,session,redirect,url_for,g
     import os
    
     app = Flask(__name__)
     app.config['SECRET_KEY'] = os.urandom(24)
    
     @app.route('/')
     def index():
         return render_template('index.html')
    
     @app.route('/login/',methods=['GET','POST'])
     def login():
         if request.method == 'GET':
             return render_template('login.html')
         else:
             username = request.form.get('username')
             password = request.form.get('password')
             if username == 'MYYD' and password == '123456':
                 session['username'] = username
                 return u'登陸成功!'
             else:
                 return u'用戶名或密碼錯誤!'
    
     @app.route('/look/')
     def look():
         if hasattr(g,'username'):
             return u'修改爲功!'
         else:
             return redirect(url_for('login'))
    
     @app.before_request
     def My_before_request():
         if session.get('username'):
             g.username = session.get('username')
    
     if __name__ == '__main__':
         app.run(debug=True)
    
    
     # index.html
    
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Index</title>
     </head>
     <body>
         <a href="{{ url_for('login') }}">點擊登陸</a>
         <a href="{{ url_for('look') }}">點擊查看</a>
     </body>
     </html>
    
    
     # login.html
    
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Login</title>
     </head>
     <body>
     <form action="" method="post">
         <table>
             <tbody>
                 <tr>
                     <td>用戶名:</td>
                     <td><input type="text" name="username"></td>
                 </tr>
                 <tr>
                     <td>密碼:</td>
                     <td><input type="password" name="password"></td>
                 </tr>
                 <tr>
                     <td></td>
                     <td><input type="submit" value="登陸"></td>
                 </tr>
             </tbody>
         </table>
     </form>
     </body>
     </html>
10.5.2 context_processor

11. 實戰練習-螞蟻有毒問答平臺

  1. 在首頁的模板 index.html 中,先粘貼如下內容:

    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
         <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
         <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  2. v3.bootcss.com 中的組件下找本身想要的導航條

相關文章
相關標籤/搜索