Flask框架的學習——12—(cookie和session、Flask上下文、常用的鉤子函數、Restful API規範、Flask-Restful插件、參數解析、 輸出字段、)

1、cookie和session

  1. cookie:在網站中,http請求是無狀態的。也就是說即使第一次和服務器連接後並且登錄成功後,第二次請求服務器依然不能知道當前請求是哪個用戶。cookie的出現就是爲了解決這個問題,第一次登錄後服務器返回一些數據(cookie)給瀏覽器,然後瀏覽器保存在本地,當該用戶發送第二次請求的時候,就會自動的把上次請求存儲的cookie數據自動的攜帶給服務器,服務器通過瀏覽器攜帶的數據就能判斷當前用戶是哪個了。cookie存儲的數據量有限,不同的瀏覽器有不同的存儲大小,但一般不超過4KB。因此使用cookie只能存儲一些小量的數據。
@app.route('/')
def index():
    # return '123'
    res = Response("首頁")
    res.set_cookie('abc', '123', max_age=3)    # key=abc, value=123, 3s後過期
    ''' set_cookie()源碼
    def set_cookie(
        self,
        key,
        value="",
        max_age=None,           # 失效時間
        expires=None,           # datetime形式的失效時間
        path="/",               # 在該路徑下才能設置cookie
        domain=None,            # 域名限制
        secure=False,           # 僅可通過HTTPS使用
        httponly=False,
        samesite=None,
    ):
    	:param key: the key (name) of the cookie to be set.
        :param value: the value of the cookie.
        :param max_age: should be a number of seconds, or `None` (default) if
                        the cookie should last only as long as the client's
                        browser session.
        :param expires: should be a `datetime` object or UNIX timestamp.
        :param path: limits the cookie to a given path, per default it will
                     span the whole domain.
        :param domain: if you want to set a cross-domain cookie.  For example,
                       ``domain=".example.com"`` will set a cookie that is
                       readable by the domain ``www.example.com``,
                       ``foo.example.com`` etc.  Otherwise, a cookie will only
                       be readable by the domain that set it.
        :param secure: If `True`, the cookie will only be available via HTTPS
    '''
    return res
    

在這裏插入圖片描述
在這裏插入圖片描述

  1. session: session和cookie的作用有點類似,都是爲了存儲用戶相關的信息。不同的是,cookie是存儲在本地瀏覽器,session是一個思路、一個概念、一個服務器存儲授權信息的解決方案,不同的服務器,不同的框架,不同的語言有不同的實現。雖然實現不一樣,但是他們的目的都是服務器爲了方便存儲數據的。session的出現,是爲了解決cookie存儲數據不安全的問題的。

  2. cookie和session結合使用:web開發發展至今,cookie和session的使用已經出現了一些非常成熟的方案。在如今的市場或者企業裏,一般有兩種存儲方式:
    存儲在服務端:通過cookie存儲一個session_id,然後具體的數據則是保存在session中。如果用戶已經登錄,則服務器會在cookie中保存一個session_id,下次再次請求的時候,會把該session_id攜帶上來,服務器根據session_id在session庫中獲取用戶的session數據。就能知道該用戶到底是誰,以及之前保存的一些狀態信息。這種專業術語叫做server side session。存儲在服務器的數據會更加的安全,不容易被竊取。但存儲在服務器也有一定的弊端,就是會佔用服務器的資源,但現在服務器已經發展至今,一些session信息還是綽綽有餘的。(Django框架採用這種模式)。
    將session數據加密,然後存儲在cookie中:這種專業術語叫做client side session。flask採用的就是這種方式,但是也可以替換成其他形式。

from flask import Flask, session
import os

print(os.urandom(24))    # 輸出的是24位隨機的字符串  當做secret_key使用

app = Flask(__name__)
# 加密session中的 secret_key
app.config['SECRET_KEY'] = os.urandom(24)


@app.route("/")
def index():
    # session  字典類型
    session['username'] = 'xxx'
    session['user_id'] = 123
    return "1231231"


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

在這裏插入圖片描述

flask中使用cookie和session

  1. cookies:在Flask中操作cookie,是通過response對象來操作,可以在response返回之前,通過response.set_cookie來設置,這個方法有以下幾個參數需要注意:
  • key:設置的cookie的key。
  • value:key對應的value。
  • max_age:改cookie的過期時間,如果不設置,則瀏覽器關閉後就會自動過期。
  • expires:過期時間,應該是一個datetime類型。
  • domain:該cookie在哪個域名中有效。一般設置子域名,比如cms.example.com。
  • path:該cookie在哪個路徑下有效。
  1. session:Flask中的session是通過from flask import session。然後添加值key和value進去即可。並且,Flask中的session機制是將session信息加密,然後存儲在cookie中。專業術語叫做client side session。
from flask import Flask, session
import os
from datetime import timedelta    # 導入時間

# print(os.urandom(24))    # 輸出的是24位隨機的字符串  當做secret_key使用

app = Flask(__name__)
# 加密session中的 secret_key
app.config['SECRET_KEY'] = os.urandom(24)
# 持久化時間設置
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)            # 2小時session過期


@app.route("/login/")
def login():
    # session  字典類型     登錄操作,記錄信息傳輸到數據庫
    session['username'] = 'xxxxxx'
    session['user_id'] = 123
    
    # 持久化  有效期
    session.permanent = True
    
    return "登錄界面"


@app.route("/")
def index():
    username = session.get('username')
    print(username)
    return "首頁界面"


@app.route("/logout/")
def logout():
    # 刪除session中的username
    session.pop('username')
    
    # 清空session中的所有數據
    session.clear()
    
    return "退出登錄"


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

2、Flask上下文

Flask項目中有兩個上下文,一個是應用上下文(app),另外一個是請求上下文(request)。請求上下文request和應用上下文current_app都是一個全局變量。所有請求都共享的。Flask有特殊的機制可以保證每次請求的數據都是隔離的,即A請求所產生的數據不會影響到B請求。所以可以直接導入request對象,也不會被一些髒數據影響了,並且不需要在每個函數中使用request的時候傳入request對象。這兩個上下文具體的實現方式和原理可以沒必要詳細瞭解。

請求上下文

  • request:請求上下文上的對象。這個對象一般用來保存一些請求的變量。比如method、args、form等。
  • session:請求上下文上的對象。這個對象一般用來保存一些會話信息。

應用上下文

  • current_app:返回當前的app。
  • g:應用上下文上的對象。處理請求時用作臨時存儲的對象。
    在這裏插入圖片描述
# -*- encoding: utf-8 -*-
"""
@File    : session_demo.py
@Time    : 2020/5/8 9:57
@Author  : chen

"""
from flask import Flask, session, request, current_app
import os
from datetime import timedelta    # 導入時間
from utils import log_a, log_b       # 導入記錄模塊

import config


# print(os.urandom(24))    # 輸出的是24位隨機的字符串  當做secret_key使用

app = Flask(__name__)             # current_app代表app
# 加密session中的 secret_key
app.config['SECRET_KEY'] = os.urandom(24)
# 持久化時間設置
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)            # 2小時session過期


@app.route("/login/")
def login():
    # session  字典類型     登錄操作,記錄信息傳輸到數據庫
    session['username'] = 'xxxxxx'
    session['user_id'] = 123
    
    # 持久化  有效期
    session.permanent = True
    
    return "登錄界面"

# 需要在視圖函數中調用   不能在視圖函數外使用
# print(current_app.name)


@app.route("/")
def index():
    username = session.get('username')
    # 採用g對象,每次刷新頁面,會被清空,臨時對象
    g.username = username
    log_a()         # 這裏可以不用傳參   g對象是全局
    log_b()
    
    # print(username)
    print(current_app.name)           # 輸出當前app的名字   需要在視圖函數中調用

    # 不是讀取config.py中的數據,查看的是app綁定的配置
    # print(current_app.config['HOST'])
    print(current_app.config['SECRET_KEY'])    # 輸出配置中的數據

    return "首頁界面"


@app.route("/logout/")
def logout():
    # 刪除session中的username
    session.pop('username')
    
    # 清空session中的所有數據
    session.clear()
    
    return "退出登錄"


if __name__ == '__main__':
    app.run(debug=True)
# -*- encoding: utf-8 -*-
"""
@File    : utils.py
@Time    : 2020/5/9 17:18
@Author  : chen

"""
# utils.py 工具文件   發送驗證碼等,隨機生成字符串等

# 記錄登錄的信息
def log_a(username):
    print("log a %s"% username)


def log_b(username):
    print("log b %s" % username)

g對象的傳參

# -*- encoding: utf-8 -*-
"""
@File    : utils.py
@Time    : 2020/5/9 17:18
@Author  : chen

"""
# utils.py 工具文件   發送驗證碼等,隨機生成字符串等

from flask import g           # 導入g對象


# 記錄登錄的信息
def log_a():
    print("log a %s"% g.username)


def log_b():
    print("log b %s" % g.username)

常用的鉤子函數

  • before_first_request:處理第一次請求之前執行。
@app.before_first_request
  def first_request():
      print 'first time request'
  • before_request:在每次請求之前執行。通常可以用這個裝飾器來給視圖函數增加一些變量。
@app.before_request
  def before_request():
      if not hasattr(g,'user'):
          setattr(g,'user','xxxx')
  • teardown_appcontext:不管是否有異常,註冊的函數都會在每次請求之後執行。
@app.teardown_appcontext
def teardown(response):
    print("teardown 被執行")
    return respons
  • context_processor:上下文處理器。返回的字典中的鍵可以在模板上下文中使用。
@app.context_processor
def context_processor():
    return {'current_user':'xxx'}
  • errorhandler:errorhandler接收狀態碼,可以自定義返回這種狀態碼的響應的處理方法。
@app.errorhandler(404)
  def page_not_found(error):
      return 'This page does not exist',404   

鉤子函數整體代碼文件hook_demo.py

# -*- encoding: utf-8 -*-
"""
@File    : hook_demo.py
@Time    : 2020/5/9 20:27
@Author  : chen

"""
from flask import Flask, render_template, abort

app = Flask(__name__)


@app.route("/")
def index():
    # 主動拋出異常的同時需要errorhandler模塊捕獲到這個異常
    abort(404)          # 此時訪問首頁的狀態碼就是404了,直接運行errorhandler(404)中的方法
    
    # 這種主動拋出異常方式無法被捕獲到
    # raise IndexError(" index error!")

    print("這是首頁")
    # return "這是首頁"
    return render_template("index.html")


# 處理第一次請求之前執行。
@app.before_first_request
def handel_first_request():                  # 只執行一次,刷新後不會再執行
    print("1  這是第一次請求之前執行的")


# before_request:在每次請求之前執行。通常可以用這個裝飾器來給視圖函數增加一些變量。
@app.before_request
def handel_before_request():
    print("2  每次請求之前執行")
    

# after_request: 在每次請求之後執行
@app.after_request
def handel_after_request(response):
    print("3  每次請求之後執行的")
    return response


#  teardown_appcontext:不管是否有異常,註冊的函數都會在每次請求之後執行。
@app.teardown_appcontext
def handel_teardown_appcontext(response):
    print("4  註冊的函數都會在每次請求之後執行")
    return response


#  context_processor:上下文處理器。返回的字典中的鍵可以在模板上下文中使用。
@app.context_processor
def handel_context_processor():
    print("5 上下文處理器。返回的字典中的鍵可以在模板上下文中使用。")
    return {"username": "abcd"}              # 將這個參數變量映射到模板html文件中,


# 錯誤異常的捕獲
#  errorhandler:errorhandler接收狀態碼,可以自定義返回這種狀態碼的響應的處理方法。
@app.errorhandler(404)
def handel_errorhandler(error):           # error必須傳遞
    print("6 errorhandler接收狀態碼,可以自定義返回這種狀態碼的響應的處理方法。")
    # return "頁面不存在", 404                    # 404網頁訪問狀態碼
    return render_template("404.html"), 404      # 404網頁訪問狀態碼


@app.errorhandler(500)
def server_error(error):           # error必須傳遞
    return "服務器錯誤!", 500      # 500 網頁響應狀態碼

    
if __name__ == '__main__':
    # 項目上線之後 部署到服務器,debug模式需要關閉,因爲報錯信息不能給客戶看
    app.run(debug=True, port=9999)

404.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>頁面不見了!!!!</p>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--   使用context_processor:上下文處理器進行參數傳遞 -->
    {{  username }}

</body>
</html>

3、Restful API規範

restful api是用於在前端與後臺進行通信的一套規範。使用這個規範可以讓前後端開發變得更加輕鬆。以下將討論這套規範的一些設計細節。

協議
採用http或者https協議。

數據傳輸格式
數據之間傳輸的格式應該都使用json,而不使用xml。

url鏈接
url鏈接中,不能有動詞,只能有名詞。並且對於一些名詞,如果出現複數,那麼應該在後面加s。

HTTP請求的方法

  • GET:從服務器上獲取資源。
  • POST:在服務器上新創建一個資源。
  • PUT:在服務器上更新資源。(客戶端提供所有改變後的數據)
  • PATCH:在服務器上更新資源。(客戶端只提供需要改變的屬性)
  • DELETE:從服務器上刪除資源。

示例:

  1. GET /users/:獲取所有用戶。
  2. POST /user/:新建一個用戶。
  3. GET /user/id/:根據id獲取一個用戶。
  4. PUT /user/id/:更新某個id的用戶的信息(需要提供用戶的所有信息)。
  5. PATCH /user/id/:更新某個id的用戶信息(只需要提供需要改變的信息)。
  6. DELETE /user/id/:刪除一個用戶。

在這裏插入圖片描述

4、Flask-Restful插件

Flask-Restful是一個專門用來寫restful api的一個插件。使用他可以快速的集成restful api功能。在app的後臺以及純api的後臺中,這個插件可以幫助我們節省很多時間。當然,如果在普通的網站中,這個插件就顯得有些雞肋了,因爲在普通的網頁開發中,是需要去渲染HTML代碼的,而Flask-Restful在每個請求中都是返回json格式的數據。

安裝
Flask-Restful需要在Flask 0.8以上的版本,在Python2.6或者Python3.3上運行。通過pip install flask-restful即可安裝。
在這裏插入圖片描述

定義Restful的視圖
如果使用Flask-Restful,那麼定義視圖函數的時候,就要繼承自flask_restful.Resource類,然後再根據當前請求的method來定義相應的方法。比如期望客戶端是使用get方法發送過來的請求,那麼就定義一個get方法。類似於MethodView。

from flask import Flask,render_template,url_for
from flask_restful import Api, Resource

app = Flask(__name__)
# 用Api來綁定app
api = Api(app)


class IndexView(Resource):    # 類視圖不同於之前繼承View
    def get(self):
        return {"username":"xxx"}         # 返回數據
    
    def post(self):
        return {"info": "登陸成功"}


api.add_resource(IndexView, '/', endpoint='index')         # 類視圖綁定

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

注意事項

  • endpoint是用來給url_for反轉url的時候指定的。如果不寫endpoint,那麼將會使用視圖的名字的小寫來作爲endpoint。
  • add_resource的第二個參數是訪問這個視圖函數的url,這個url可以跟之前的route一樣,可以傳遞參數。並且還有一點不同的是,這個方法可以傳遞多個url來指定這個視圖函數。

在這裏插入圖片描述
Postman軟件測試post方法傳遞參數是否連接成功
在這裏插入圖片描述

5、 參數解析

Flask-Restful插件提供了類似WTForms來驗證提交的數據是否合法的包,叫做reqparse。

在這裏插入圖片描述
add_argument可以指定這個字段的名字,這個字段的數據類型等。

  • default:默認值,如果這個參數沒有值,那麼將使用這個參數指定的值。
  • required:是否必須。默認爲False,如果設置爲True,那麼這個參數就必須提交上來。
  • type:這個參數的數據類型,如果指定,那麼將使用指定的數據類型來強制轉換提交上來的值。
  • choices:選項。提交上來的值只有滿足這個選項中的值才符合驗證通過,否則驗證不通過。
  • help:錯誤信息。如果驗證失敗後,將會使用這個參數指定的值作爲錯誤信息。
  • trim:是否要去掉前後的空格。
from flask import Flask, render_template, url_for
from flask_restful import Api, Resource, reqparse, inputs   # reqparse 類似WTForms來驗證提交的數據是否合法 ,inputs驗證email,url等數據

app = Flask(__name__)
# 用Api來綁定app
api = Api(app)


class IndexView(Resource):  # 類視圖不同於之前繼承View
    def get(self):
        return {"username": "xxx"}  # 返回數據
    
    def post(self):
        # 類似WTForms來驗證提交的數據是否合法
        parse = reqparse.RequestParser()
        # 傳參     help=錯誤信息     required=True必須提供該參數
        parse.add_argument('username', type=str, help='用戶名驗證錯誤', required=True)
        parse.add_argument('password', type=str, help='用戶名密碼錯誤', required=True)
        parse.add_argument('age', type=int, help='用戶年齡錯誤')
        parse.add_argument('gender', type=str, help='用戶性別錯誤', choices=['male', 'female'])
        
        # url驗證\郵箱驗證
        parse.add_argument('homepage', type=inputs.url, help='網頁鏈接錯誤', required=True)
        # regex  正則表達式   手機號碼驗證
        parse.add_argument('phone', type=inputs.regex(r'1[3456]\d{9}'), help='手機號碼錯誤', required=True)

        # 驗證參數是否傳遞成功
        args = parse.parse_args()
        print(args)
        
        return {"info": "登陸成功"}


api.add_resource(IndexView, '/', endpoint='index')  # 類視圖綁定

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

6、 輸出字段

對於一個視圖函數,你可以指定好一些字段用於返回。以後可以使用ORM模型或者自定義的模型的時候,他會自動的獲取模型中的相應的字段,生成json數據,然後再返回給客戶端。這其中需要導入flask_restful.marshal_with裝飾器。並且需要寫一個字典,來指示需要返回的字段,以及該字段的數據類型。

from flask import Flask, render_template, url_for
from flask_restful import Api, Resource, reqparse, inputs  # reqparse 類似WTForms來驗證提交的數據是否合法 ,inputs驗證email,url等數據
from flask_restful import fields, marshal_with      # fields用於輸出字段   marshal_with裝飾器關聯返回字段信息
app = Flask(__name__)
# 用Api來綁定app
api = Api(app)


class ArticleView(Resource):      # 類視圖不同於之前繼承View,這裏需要繼承Resource
    resource_fields = {           # 返回字段信息
        'title': fields.String,
        'content': fields.String,
    }
    
    @marshal_with(resource_fields)         # 關聯返回字段信息
    def get(self):
        return {'title': 'abcdefg'}


api.add_resource(ArticleView, '/article/', endpoint='article')  # 類視圖綁定

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

在這裏插入圖片描述

重命名屬性
很多時候你面向公衆的字段名稱是不同於內部的屬性名。使用 attribute可以配置這種映射。比如現在想要返回user.school中的值,但是在返回給外面的時候,想以education返回回去,那麼可以這樣寫

resource_fields = {
    'education': fields.String(attribute='school')
}

默認值
在返回一些字段的時候,有時候可能沒有值,那麼這時候可以在指定fields的時候給定一個默認值

resource_fields = {
    'age': fields.Integer(default=18)
}

複雜結構
有時候想要在返回的數據格式中,形成比較複雜的結構。那麼可以使用一些特殊的字段來實現。比如要在一個字段中放置一個列表,那麼可以使用fields.List,比如在一個字段下面又是一個字典,那麼可以使用fields.Nested。

class ProfileView(Resource):
    resource_fields = {
        'username': fields.String,
        'age': fields.Integer,
        'school': fields.String,
        'tags': fields.List(fields.String),
        'more': fields.Nested({
            'signature': fields.String
        })
    }

實例代碼如下

主體代碼文件: flask_restful_demo1.py

from flask import Flask, render_template, url_for
from flask_restful import Api, Resource, reqparse, inputs  # reqparse 類似WTForms來驗證提交的數據是否合法 ,inputs驗證email,url等數據
from flask_restful import fields, marshal_with  # fields用於輸出字段   marshal_with裝飾器關聯返回字段信息

import config    # 導入配置文件
from exts import db      # 第三方文件防止互相引用

from model import Article    # 導入模型

app = Flask(__name__)

# 加載配置
app.config.from_object(config)
db.init_app(app)                      # 綁定app

# 用Api來綁定app
api = Api(app)


class ArticleView(Resource):  # 類視圖不同於之前繼承View,這裏需要繼承Resource
    resource_fields = {  # 返回字段信息
        # 'title': fields.String,
        # 修改給前端返回的字段信息   原本的模型中的屬性或者叫字段爲title,現在返回給前端的是article_title
        'article_title': fields.String(attribute='title'),
        
        'content': fields.String,
        # 'author': fields.String,
        'author': fields.Nested({               # 一對多關係
            "username": fields.String,
            "email": fields.String,
        }),
        
        # 'tag': fields.String,
        'tag': fields.List(fields.Nested({      # 多對多關係
            "id": fields.Integer,
            "name": fields.String,
        })),

        # 模型中沒有的字段名和設置默認值
        'read_count': fields.Integer(default=0)
    }
    
    @marshal_with(resource_fields)  # 關聯返回字段信息
    def get(self, article_id):       # 傳參
        article = Article.query.get(article_id)
        return article                  # 返回模型,返回的數據中包含模型中的所有字段
        # return {'title': 'abcdefg'}


api.add_resource(IndexView, '/', endpoint='index')  # 類視圖綁定
# api.add_resource(ArticleView, '/article/', endpoint='article')  # 類視圖綁定
api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')  # 傳參的時候路由也一起改變


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

模型文件model.py

# -*- encoding: utf-8 -*-
"""
@File    : model.py
@Time    : 2020/5/10 17:09
@Author  : chen

"""
from exts import db

# 用戶
# 文章         用戶與文章是一對多關係
# 標籤         文章與標籤是多對多關係     此時需要建立中間表
class User(db.Model):
    __tablename__ = 'user'
    
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50))
    email = db.Column(db.String(50))


# 中間表的創建   創建article和tag的中間表
article_tag_table = db.Table(
    'article_tag',
    db.Column('article_id', db.Integer, db.ForeignKey('article.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
)


class Article(db.Model):
    __tablename__ = 'article'
    
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    context = db.Column(db.String(50))
    
    # 外鍵
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    # 反向引用
    author = db.relationship('User', backref='articles')
    
    # 中間表建立連接關係
    tags = db.relationship('Tag', secondary=article_tag_table, backref='tags')
    

class Tag(db.Model):
    __tablename__ = 'tag'
    
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))    

配置文件config.py

# -*- encoding: utf-8 -*-
"""
@File    : config.py
@Time    : 2020/5/10 17:08
@Author  : chen

"""
HOSTNAME = '127.0.0.1'
DATABASE = 'demo0510'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'

DB_URL = 'mysql+mysqlconnector://{}:{}@{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

# engine = create_engine(DB_URL)
SQLALCHEMY_DATABASE_URI = DB_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False

第三方引用文件exts.py :防止互相引用報錯

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

映射到數據庫文件manage.py

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/5/10 17:36
@Author  : chen

"""
from flask_script import Manager
from flask_restful_demo import app     # 需要將當前文件夾設置爲當前根目錄,纔不會報錯
from flask_migrate import Migrate, MigrateCommand
from exts import db

# 導入模型 才能映射到數據庫
import model

manage = Manager(app)

Migrate(app, db)
manage.add_command('db', MigrateCommand)

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

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章