最近看了Flask Web開(kāi)發(fā):基于Python的Web應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)锐墙,書中詳細(xì)介紹了Web程序的開(kāi)發(fā)礁哄、測(cè)試、部署過(guò)程溪北,值得一讀桐绒!我在書中例子的基礎(chǔ)上做了些更改,實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的個(gè)人博客:NiceBlog之拨,僅作為個(gè)人學(xué)習(xí)茉继,還有許多不足的地方待完善,這里做一些簡(jiǎn)單的記錄蚀乔,方便以后查閱烁竭,代碼放在了Github上:https://github.com/SheHuan/NiceBlog。
一吉挣、功能
1派撕、對(duì)于普通用戶,主要有如下功能:
- 注冊(cè)睬魂、登錄终吼、重置密碼(郵箱驗(yàn)證)
- 文章列表、詳情
- 評(píng)論
- 喜歡
2氯哮、對(duì)于管理員际跪,除了有普通用戶的功能,主要有如下功能:
- 寫文章(Markdown編輯)
- 用戶權(quán)限管理(管理喜歡喉钢、評(píng)論的權(quán)限)
- 評(píng)論管理(刪除垫卤、屏蔽)
3、為移動(dòng)端提供相關(guān)api接口
二出牧、項(xiàng)目結(jié)構(gòu)
遵循了書中多文件Flask程序的基本結(jié)構(gòu),下邊是NiceBlog的項(xiàng)目結(jié)構(gòu):
|-NiceBlog
???|-app/ 主目錄
??????|-api/ 為移動(dòng)端提供接口的藍(lán)本
??????|-auth/ 權(quán)限認(rèn)證的藍(lán)本
??????|-main/ 主體功能的藍(lán)本
??????|-manage/ 管理相關(guān)功能的藍(lán)本
??????|-static/ 靜態(tài)資源目錄(icon歇盼、js舔痕、css)
??????|-templates/ html模板目錄
??????|-__init__.py 初始化項(xiàng)目的工廠函數(shù)
??????|-decorators.py 自定義的裝飾器
??????|-email.py 發(fā)送郵件功能
??????|-excepitions.py 自定義異常處理
??????|-models.py 數(shù)據(jù)模型
???|-migrations/ 數(shù)據(jù)庫(kù)遷移腳本目錄
???|-nb_env/ 虛擬環(huán)境
???|-tests/ 單元測(cè)試目錄
???|-config.py 配置文件
???|-manage.py 啟動(dòng)程序以及其他的程序任務(wù)
???|-requirements.txt 項(xiàng)目的依賴包列表
三、實(shí)現(xiàn)
1豹缀、工廠函數(shù)
一個(gè)簡(jiǎn)單的Flask Web程序可以寫在單文件中伯复,test.py
:
app = Flask(__name__)
# 定義的路由
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
if __name__ == '__main__':
app.run()
但是執(zhí)行程序時(shí),由于在全局作用域創(chuàng)建導(dǎo)致無(wú)法動(dòng)態(tài)修改配置邢笙,也導(dǎo)致了單元測(cè)試時(shí)無(wú)法在不同配置環(huán)境運(yùn)行程序啸如。所以可以把程序的創(chuàng)建轉(zhuǎn)移到可顯示調(diào)用的工廠函數(shù)中,也就是前邊項(xiàng)目結(jié)構(gòu)中的__init__.py
氮惯,在工廠函數(shù)中導(dǎo)入需要的Flask擴(kuò)展:
def create_app(config_name):
app = Flask(__name__)
# 導(dǎo)致指定的配置對(duì)象
app.config.from_object(config[config_name])
# 調(diào)用config.py的init_app()
config[config_name].init_app(app)
# 初始化擴(kuò)展
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
pagedown.init_app(app)
return app
2叮雳、藍(lán)本
新的問(wèn)題來(lái)了想暗,使用工廠函數(shù)后,程序在運(yùn)行時(shí)創(chuàng)建帘不,而不是在全局作用域说莫,必須等到執(zhí)行create_app()
后才能使用@app.route()
裝飾器,這時(shí)就要使用藍(lán)本了寞焙,在藍(lán)本中也可以定義路由储狭,但是定義的路由處于休眠狀態(tài)直到藍(lán)本注冊(cè)到程序后在成為程序一部分,例如main藍(lán)本的目錄結(jié)構(gòu)如下:
|-NiceBlog
???|-app/ 主目錄
??????|-main/ 主體功能的藍(lán)本
?????????|-__init__.py 創(chuàng)建藍(lán)本
?????????|-errors.py 藍(lán)本的錯(cuò)誤處理
?????????|-forms.py 藍(lán)本的表單
?????????|-views.py 藍(lán)本的路由
首先看一下__init__.py
:
# 兩個(gè)參數(shù)分別指定藍(lán)本的名字捣郊、藍(lán)本所在的包或模塊(使用 __name__即可)
main = Blueprint('main', __name__)
# 導(dǎo)入路由模塊辽狈、錯(cuò)誤處理模塊,將其和藍(lán)本關(guān)聯(lián)起來(lái)
# 在藍(lán)本的末尾導(dǎo)入在兩個(gè)模塊里還要導(dǎo)入藍(lán)本呛牲,防止循環(huán)導(dǎo)入依賴
from app.main import views, errors
2.1刮萌、表單
forms.py
是當(dāng)前藍(lán)本中的表單,項(xiàng)目中使用了FlaskForm
侈净,可以方便的完成表單校驗(yàn)尊勿,例如創(chuàng)建、編輯文章的表單:
class BlogForm(FlaskForm):
title = StringField('請(qǐng)輸入文章標(biāo)題', validators=[DataRequired(), Length(1, 128)])
labels = StringField('文章標(biāo)簽(標(biāo)簽之間用空格隔開(kāi))', validators=[DataRequired()])
summary = TextAreaField('文章概要', validators=[DataRequired()])
content = TextAreaField('文章內(nèi)容', validators=[DataRequired()])
preview = TextAreaField('文章預(yù)覽', validators=[DataRequired()])
publish = SubmitField('發(fā)布')
save = SubmitField('保存')
2.2畜侦、路由
views.py
就是在藍(lán)本中定義的路由元扔,例如主頁(yè)的路由:
@main.route('/create-blog', methods=['GET', 'POST'])
@admin_required
def create_blog():
"""
寫新文章
"""
form = BlogForm()
if form.validate_on_submit():
blog = None
if form.publish.data:
# 發(fā)布
elif form.save.data:
# 保存草稿
return redirect(url_for('main.index'))
return render_template('markdown_editor.html', form=form, type='create')
注意裝飾器為當(dāng)前藍(lán)本的名字main
,而不是之前的app
旋膳。create_blog()
稱為視圖函數(shù)澎语,一個(gè)路由保存了URL到視圖函數(shù)的映射關(guān)系。redirect(url_for('main.index'))
代表重定向到主頁(yè)验懊,url_for()
的參數(shù)為要跳轉(zhuǎn)到的URL對(duì)應(yīng)的視圖函數(shù)名擅羞,但需要加上視圖函數(shù)所在的藍(lán)本名,即main.index
义图。render_template()
是Flask提供的函數(shù)减俏,把Jinja2
模板引擎集成到了程序中,第一個(gè)參數(shù)是模板名稱對(duì)應(yīng)一個(gè)html文件碱工,即執(zhí)行該視圖函數(shù)后最終要渲染的頁(yè)面娃承,后邊的參數(shù)為傳遞給模板的參數(shù)。
2.3怕篷、錯(cuò)誤處理
errors.py
是藍(lán)本中的錯(cuò)誤處理程序历筝,例如:
@main.app_errorhandler(404)
def page_not_found(e):
if request.url.find('api') != -1:
return jsonify({'error': '請(qǐng)求的資源不存在', 'code': '404', 'data': ''})
return render_template('error/404.html'), 404
如果使用@main.errorhandler
裝飾器只有當(dāng)前藍(lán)本的錯(cuò)誤才能觸發(fā),為了使其它錯(cuò)誤也能觸發(fā)所以使用了@main.app_errorhandler
裝飾器
2.4廊谓、注冊(cè)藍(lán)本
其它藍(lán)本的定義也類似梳猪,最后需要在工廠函數(shù)中重注冊(cè)藍(lán)本,例如:
def create_app(config_name):
# ......
# 注冊(cè)main藍(lán)本
from app.main import main as main_blueprint
app.register_blueprint(main_blueprint)
# 注冊(cè)auth藍(lán)本
from app.auth import auth as auth_blueprint
# 使用url_prefix注冊(cè)后蒸痹,藍(lán)本中定義的所有路由都會(huì)加上指定前綴春弥,/login --> /auth/login
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app
3诈茧、前端
3.1里伯、Jinja2
Flask使用Jinja2
作為模板引擎任岸,模板是一個(gè)包含響應(yīng)文本的HTML文件纤虽,其中包含只有在請(qǐng)求的上下文才知道的動(dòng)態(tài)占位變量。默認(rèn)情況下俺祠,模板保存在templates
目錄公给。
在Jinja2
模板中{{ 變量名 }}
代表一個(gè)變量(注意變量名兩邊有一個(gè)空格,可以識(shí)別任意類型的變量)蜘渣,從渲染模板時(shí)使用的數(shù)據(jù)中獲取淌铐。如果變量的值是HTML,由于轉(zhuǎn)義的原因?qū)е聻g覽器不能正常顯示HTML代碼蔫缸,所以需要使用safe
過(guò)濾器腿准,例如文章詳情的HTML顯示就需要這樣處理,過(guò)濾器寫在變量名后用豎線隔開(kāi){{ 變量名|過(guò)濾器名 }}
Jinja2
中用{% 控制語(yǔ)句 %}
代表控制結(jié)構(gòu)來(lái)改變模板的渲染流程拾碌,例如:
# 條件控制
{% if xxx %}
<h1>Android</h1>
{% else %}
<h1>iOS</h1>
{% endif %}
# for循環(huán)
{% for x in xs %}
<li>{{ x }}</li>
{% endfor %}
# 導(dǎo)入
{% import 'xxx.html' %}
# 包含
{% include 'xxx.html' %}
導(dǎo)入吐葱、包含的目的都是為了復(fù)用,還可以通過(guò)繼承實(shí)現(xiàn)復(fù)用校翔,類似于類的繼承:
# 繼承
{% extends "base.html" %}
通過(guò)繼承弟跑,模板中重復(fù)的代碼都可以寫在父模板里,例如導(dǎo)航條和頁(yè)面底部footer就可以放在父模板里防症。
3.2孟辑、Bootstrap
前端使用了Bootstrap框架,它提供了良好的CSS規(guī)范蔫敲,可以幫助我們更好的美化界面饲嗽,具體的可參考:
https://v3.bootcss.com/,要在項(xiàng)目中集成它可以使用Flask的Flask-Bootstrap
擴(kuò)展奈嘿,直接在PyCharm安裝貌虾,并在工廠函數(shù)中初始化,還要讓項(xiàng)目的父模板繼承Bootstrap的基類模板:
# common_base.html
{% extends "bootstrap/base.html" %}
Bootstrap的基類模板base.html
提供了一個(gè)網(wǎng)頁(yè)框架裙犹,包含了Bootstrap中的所有CSS和JS文件酝惧。除此之外基類模板還定義了許多可在其子類模板中重定義的塊,使用格式如下:
{% block 塊名稱 %}
{% endblock %}
常用的塊如下:
塊名稱 | 含義 |
---|---|
head | <head>標(biāo)簽中的內(nèi)容 |
title | <title>標(biāo)簽中的內(nèi)容 |
body | <body>標(biāo)簽中的內(nèi)容 |
styles | css樣式單的定義 |
navbar | 自定義的導(dǎo)航條 |
content | 自定義的頁(yè)面內(nèi)容 |
page_content | 定義content在內(nèi)部 |
scripts | JS聲明伯诬,一般在模板尾部 |
注意如在子模板在模板已有的塊中添加新內(nèi)容,需要使用super()
函數(shù):
{% block scripts %}
{{ super() }}
<!-- 新加的內(nèi)容 -->
{% endblock %}
3.3巫财、Flask-WTF
在2.1中我們已經(jīng)看到了用Flask-WTF
定義表單的方式盗似,即自定義的表單類繼承FlaskForm
類,并添加需要的類變量平项,Flask-WTF
定義了許多標(biāo)準(zhǔn)字段可以被渲染成指定的表單類HTML標(biāo)簽赫舒,例如:
字段名 | 對(duì)應(yīng)的H5標(biāo)簽 |
---|---|
StringField | 文本框 |
TextAreaField | 多行文本框 |
PasswordField | 密碼輸入框 |
BooleanField | 復(fù)選框 |
SubmitField | 表單提交按鈕 |
同時(shí)Flask-WTF
還提供了許多常用的表單校驗(yàn)函數(shù)悍及,例如:Email()
、EqualTo()
接癌、DataRequired()
心赶、Length()
等等,當(dāng)點(diǎn)擊提交按鈕時(shí)缺猛,會(huì)自動(dòng)校驗(yàn)表單是否滿足預(yù)定義的條件缨叫。
在2.2中,我們通過(guò)參數(shù)把表單類的實(shí)例同步form
參數(shù)傳入模板:
render_template('markdown_editor.html', form=form, type='create')
在模板中可以通過(guò)如下方式生表單(只保留了部分核心代碼):
<form method="post" role="form" class="height-full">
{{ form.hidden_tag() }}
{{ form.title(id="title", class="form-control editor-blog-title", placeholder=form.title.label.text) }}
{{ form.labels(class="form-control editor-blog-area", placeholder=form.labels.label.text) }}
{{ form.summary(class="form-control editor-blog-area", placeholder=form.summary.label.text, rows=3) }}
{{ form.publish(class="btn btn-info") }}
{{ form.save(class="btn btn-success") }}
</form>
這樣的好處是我們能自定義表單的樣式等等荔燎,但是工作量蠻大的耻姥,如果對(duì)表單樣式?jīng)]有特殊的需求,Bootstrap中的表單樣式可以滿足需求有咨,可以通過(guò)Flask-Bootstrap
提供的輔助函數(shù)快速的渲染表單琐簇,只需要如下兩步:
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
例如登錄的H5模板就是這樣做的。
form.hidden_tag()
模板參數(shù)將被替換為一個(gè)隱藏字段座享,用來(lái)實(shí)現(xiàn)在配置中激活的 CSRF 保護(hù)婉商。如果你已經(jīng)激活了CSRF,這個(gè)字段需要出現(xiàn)在你所有的表單中渣叛。
在2.2中丈秩,如果點(diǎn)擊表單提交按鈕,所有的表單都能成功通過(guò)校驗(yàn)诗箍,則form.validate_on_submit()
的值為True
癣籽,否則校驗(yàn)失敗,網(wǎng)頁(yè)會(huì)出現(xiàn)對(duì)應(yīng)提示滤祖。如果有兩個(gè)提交按鈕筷狼,那么在校驗(yàn)成功后,還需要判斷點(diǎn)擊的是哪個(gè)按鈕匠童,否則所有的按鈕都執(zhí)行了同一個(gè)操作埂材。例如我們的文章發(fā)布和保存按鈕,當(dāng)表單類中的按鈕字段的data
屬性為True
則代表該按鈕被點(diǎn)擊汤求,例如:
if form.publish.data:
# 發(fā)布
elif form.save.data:
# 保存草稿
3.4俏险、jQuery
有些頁(yè)面需要在相關(guān)操作后修改控件的CSS樣式,例如文章詳情的喜歡和取消喜歡按鈕扬绪,最簡(jiǎn)單的方式是操作成功后直接刷新整個(gè)頁(yè)面竖独,但這樣體驗(yàn)并不好,更好的方式是局部刷新挤牛。這里直接使用jQuery(Bootstrap也提供了類似的操作莹痢,同時(shí)包含了jQuery,不需要單獨(dú)導(dǎo)入jQuery)來(lái)實(shí)現(xiàn)。使用jQuery
強(qiáng)大的選擇器功能可以方便的得到要操作的DOM節(jié)點(diǎn)
竞膳,按鈕的點(diǎn)擊也是發(fā)起一個(gè)請(qǐng)求航瞭,jQuery
也集成了ajax
,可以方便的處理請(qǐng)求坦辟,在請(qǐng)求完成后根據(jù)響應(yīng)結(jié)果來(lái)更改DOM節(jié)點(diǎn)
的樣式刊侯。看下按鈕的點(diǎn)擊事件:
favourite = function (id) {
if ($('.blog-favourite-btn').length > 0) {//取消喜歡
$.get('/manage/blog/cancel_favourite', {
id: id
}).done(function (data) {
$('.blog-favourite-btn span').removeClass('glyphicon-heart').addClass('glyphicon-heart-empty');
$('.blog-favourite-btn').removeClass('blog-favourite-btn').addClass('blog-unfavourite-btn');
})
} else if ($('.blog-unfavourite-btn').length > 0) {//喜歡
$.get('/manage/blog/favourite', {
id: id
}).done(function (data) {
if ('200' === data) {
$('.blog-unfavourite-btn span').removeClass('glyphicon-heart-empty').addClass('glyphicon-heart');
$('.blog-unfavourite-btn').removeClass('blog-unfavourite-btn').addClass('blog-favourite-btn');
}
if ('403' === data) {
alert('沒(méi)有操作權(quán)限');
}
})
}
}
4锉走、Markdown
書中使用的是Flask-PageDown
滨彻、Markdown
兩個(gè)庫(kù)來(lái)實(shí)現(xiàn)對(duì)Markdown功能的支持,但是不夠理想挠日,有些Markdown語(yǔ)法并不能很好的支持疮绷,例如Flask-PageDown
實(shí)時(shí)預(yù)覽時(shí)并不支持代碼塊和表格等。最后使用了marked這個(gè)庫(kù)嚣潜,它是一個(gè)全功能的Markdown解析器和編譯器冬骚,用JavaScript編寫,構(gòu)建速度快懂算,其實(shí)就是實(shí)時(shí)將用Markdown語(yǔ)法編輯的內(nèi)容轉(zhuǎn)換成對(duì)應(yīng)的HTML預(yù)覽只冻,但是沒(méi)有CSS樣式的HTML還是有點(diǎn)丑,github-markdown-css是一個(gè)不錯(cuò)的選擇计技,可以幫助我們實(shí)現(xiàn)github風(fēng)格的Markdwon預(yù)覽喜德。既然是要編輯文章那么直接使用HTML里的<textarea>
肯定難以實(shí)現(xiàn)理想的效果,這里使用了ace垮媒,它是一個(gè)用JavaScript編寫的獨(dú)立代碼編輯器舍悯,下載ace-builds/arc-min
即可。核心的幫助工具就這些了睡雇,接下來(lái)就是把他們組合起來(lái)萌衬,首先看HTML界面主要有編輯和預(yù)覽兩部分:
<!--編輯-->
<div class="col-md-6 markdown-panel">
<div id="markdown-edit"></div>
</div>
<!--預(yù)覽-->
<div class="col-md-6 markdown-panel">
<div id="markdown-preview" class="markdown-body"></div>
</div>
接下來(lái)就是編輯器的初始化了:
<script>
//編輯器配置
var ace_edit = ace.edit('markdown-edit');
ace_edit.setTheme('ace/theme/chrome');
ace_edit.getSession().setMode('ace/mode/markdown');
ace_edit.renderer.setShowPrintMargin(false);
//字體大小
ace_edit.setFontSize(15);
//自動(dòng)換行
ace_edit.setOption('wrap', 'free');
$("#markdown-edit").keyup(function () {
// 實(shí)現(xiàn)Markdown到HTML的預(yù)覽
$("#preview").text(marked(ace_edit.getValue()));
});
</script>
更多細(xì)節(jié)可參考markdown_editor.html,看一下效果:
5它抱、數(shù)據(jù)庫(kù)
數(shù)據(jù)庫(kù)使用的是MySql
秕豫,同時(shí)使用了ORM
框架SQLAlchemy
把關(guān)系數(shù)據(jù)庫(kù)的表結(jié)構(gòu)映射到對(duì)象上,來(lái)簡(jiǎn)化數(shù)據(jù)庫(kù)的操作观蓄,F(xiàn)lask有一個(gè)Flask-SQLAlchemy
擴(kuò)展可以方便的在程序中使用SQLAlchemy
混移,首先需要指定數(shù)據(jù)庫(kù)URL,這一步在config.py
完成:
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@127.0.0.1:3306/niceblog_dev'
然后在工廠函數(shù)完成配置侮穿。之后就是定義數(shù)據(jù)模型了歌径,項(xiàng)目中一共定義了6個(gè)數(shù)據(jù)模型:User
、Role
亲茅、Blog
沮脖、Comment
金矛、Favourite
、Label
勺届,都繼承db.Model
,在數(shù)據(jù)模型中指定表名稱娶耍、列名稱等信息免姿,例如保存文章信息的Blog
:
class Blog(db.Model):
"""
博客數(shù)據(jù)Model
"""
__tablename__ = 'blogs'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
summary = db.Column(db.Text)
content = db.Column(db.Text)
content_html = db.Column(db.Text)
# 發(fā)布日期
publish_date = db.Column(db.DateTime, index=True)
# 最后的編輯日期
edit_date = db.Column(db.DateTime, index=True)
# 外鍵,和User表對(duì)應(yīng)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
# 是否是草稿
draft = db.Column(db.Boolean)
# 是否禁用評(píng)論
disable_comment = db.Column(db.Boolean, default=False)
# 被瀏覽的次數(shù)
views = db.Column(db.Integer, default=0)
comments = db.relationship('Comment', backref='blog', lazy='dynamic')
favourites = db.relationship('Favourite', backref='blog', lazy='dynamic')
配置好了數(shù)據(jù)庫(kù)榕酒、定義好了數(shù)據(jù)模型胚膊,就可以通過(guò)如下命令來(lái)操作數(shù)據(jù)庫(kù)了:
-
db.create_all()
:創(chuàng)建表 -
db.session.add()
:插入行、修改行想鹰,最后需要執(zhí)行db.session.commit()
-
db.session.delete()
:刪除行紊婉,最后需要執(zhí)行db.session.commit()
-
數(shù)據(jù)模型名.query().查詢過(guò)濾器.查詢執(zhí)行函數(shù)
:查詢行
常用的查詢過(guò)濾器有:filter()
、filter_by()
辑舷、limit
喻犁、offset()
、order_by()
何缓、group_by()
常用的查詢執(zhí)行函數(shù)有:all()
肢础、first()
、first_or_404()
碌廓、get()
传轰、get_or_404()
、count()
谷婆、paginate()
如果在shell中操作數(shù)據(jù)庫(kù)慨蛙,每次都要導(dǎo)入數(shù)據(jù)庫(kù)實(shí)例和數(shù)據(jù)模型,如何避免這個(gè)問(wèn)題呢纪挎?由于項(xiàng)目使用了Flask-Script
命令行解釋器期贫,支持自定義命令,可以讓Flask-Script
的shell命令自動(dòng)導(dǎo)入特定對(duì)象即可:
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role, Blog=Blog, Comment=Comment, Favourite=Favourite, Label=Label,
Permission=Permission)
manager.add_command('shell', Shell(make_context=make_shell_context))
開(kāi)發(fā)中修改數(shù)據(jù)模型是不可避免的廷区,為了不發(fā)生刪表重建導(dǎo)致數(shù)據(jù)丟失的問(wèn)題唯灵,我們需要使用數(shù)據(jù)庫(kù)遷移框架
,增量式的把數(shù)據(jù)模型的改變應(yīng)用到數(shù)據(jù)庫(kù)中隙轻,我們可以直接使用Flask-Migrate
來(lái)完成埠帕,在Flask-Script
集成數(shù)據(jù)庫(kù)遷移功能:
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
數(shù)據(jù)庫(kù)遷移只要有如下三個(gè)命令:
-
python manage.py db init
:創(chuàng)建遷移倉(cāng)庫(kù),初始執(zhí)行一次即可 -
python manage.py db migrate --message "initial migration"
:創(chuàng)建遷移腳本 -
python manage.py db upgrade
:更新數(shù)據(jù)庫(kù)
每次修改數(shù)據(jù)模型后需要更新數(shù)據(jù)庫(kù)時(shí)執(zhí)行命令2玖绿、3即可敛瓷。
6、接口開(kāi)發(fā)
api藍(lán)本的目錄結(jié)構(gòu)如下:
|-NiceBlog
???|-app/ 主目錄
??????|-api/ 為移動(dòng)端提供接口的藍(lán)本
?????????|-__init__.py 創(chuàng)建藍(lán)本
?????????|-authentication.py 登錄斑匪、注冊(cè)呐籽、token檢驗(yàn)
?????????|-blogs.py 文章列表、詳情的接口
?????????|-comments.py 評(píng)論相關(guān)接口
?????????|-decorators.py 自定義裝飾器
?????????|-favourites.py 喜歡操作相關(guān)的接口
?????????|-labels.py 文章分類標(biāo)簽接口
?????????|-responses.py 幫助返回JSON數(shù)據(jù)
Flask提供的jsonify()
函數(shù)可以方便的把一個(gè)字典轉(zhuǎn)換成JSON串返,例如返回文章分類標(biāo)簽的路由可以這么寫:
@api.route('/labels/')
def get_labels():
labels = Label.query.all()
data = {'labels': [label.to_json() for label in labels]}
return jsonify({'error': '', 'code': '200', 'data': data})
to_json()
方法是數(shù)據(jù)模型中Label
類的方法狡蝶,完成數(shù)據(jù)模型到JSON格式化的序列化字典轉(zhuǎn)換:
def to_json(self):
json_label = {
'id': self.id,
'name': self.name,
}
return json_label
為了保證接口有一定的安全性庶橱,不被隨意訪問(wèn),除了登錄贪惹、注冊(cè)苏章、以及文章預(yù)覽的html頁(yè)面外其他接口都需要一個(gè)token參數(shù),token可以在登錄后得到奏瞬,token過(guò)期后需要重新請(qǐng)求枫绅。由于要對(duì)請(qǐng)求攜帶的token參數(shù)校驗(yàn),可以定義一個(gè)before_request
鉤子硼端,在每次請(qǐng)求前統(tǒng)一完成token的校驗(yàn):
@api.before_request
def before_request():
url = request.url
if url.find('login') == -1 and url.find('register') == -1 and url.find('preview') == -1:
token = request.args.get('token')
if token is None:
return unauthorized('token缺失')
user = User.verify_auth_token(token)
if user is None:
return forbidden('token過(guò)期并淋,請(qǐng)重新登錄')
else:
# g是程序上下文,用作臨時(shí)存儲(chǔ)對(duì)象珍昨,
# 保存當(dāng)前的請(qǐng)求對(duì)應(yīng)的user县耽,每次請(qǐng)求都會(huì)更新
g.current_user = user
測(cè)試接口可以使用HTTPie,通過(guò)PyCharm在虛擬環(huán)境安裝HTTPie后曼尊,啟動(dòng)Web服務(wù)酬诀,windows下通過(guò)cmd進(jìn)入虛擬環(huán)境目錄,執(zhí)行Scripts\activate
激活虛擬環(huán)境(退出虛擬環(huán)境執(zhí)行deactivate
):
執(zhí)行登錄請(qǐng)求骆撇,命令如下:
http POST http://127.0.0.1:5000/api/login/ email==shehuan320@163.com password==123456
響應(yīng)如下:
使用登錄的得到的token請(qǐng)求文章分類標(biāo)簽接口:
http GET http://127.0.0.1:5000/api/labels/ token==eyJhbGciOiJIUzI1NiIsImlhdCI6MTUxODEzOTQwNiwiZXhwIjoxNTE4NzQ0MjA2fQ.eyJpZCI6MX0.EujL1Pb4lg20Bb2QWngop1N79os0LdFWniA8bL4JQHo
響應(yīng)如下:
四瞒御、安裝
以下的安裝步驟是基于Windows環(huán)境的!
- 從Guthub clone NiceBlog到本地
- 安裝Python 3 的開(kāi)發(fā)環(huán)境
- 安裝PyCharm開(kāi)發(fā)工具神郊,導(dǎo)入項(xiàng)目肴裙,建議使用虛擬環(huán)境,可直接在 PyCharm 中創(chuàng)建一個(gè)虛擬環(huán)境涌乳,或者使用命令行創(chuàng)建蜻懦。
- 在虛擬環(huán)境中安裝
requestments.txt
中的擴(kuò)展包,直接在 PyCharm 的 Terminal 執(zhí)行如下命令:
pip install -r requirements.txt
- 安裝MySql夕晓,創(chuàng)建數(shù)據(jù)庫(kù)宛乃,并在
config.py
中替換為自己創(chuàng)建的數(shù)據(jù)名,并修改用戶名和密碼 - 在 Terminal 執(zhí)行
python manage.py shell
切換到 shell 環(huán)境蒸辆,再執(zhí)行db.create_all()
創(chuàng)建數(shù)據(jù)表 - 由于注冊(cè)賬號(hào)使用了qq郵箱驗(yàn)證征炼,請(qǐng)?jiān)?code>config.py中替換自己的qq郵箱和授權(quán)登錄密碼,并更改管理員郵箱為自己的郵箱躬贡。
- 執(zhí)行
exit()
退出 shell 環(huán)境谆奥,再執(zhí)行python manage.py runserver
就可以啟動(dòng) Web 服務(wù)了,默認(rèn)運(yùn)行在http://127.0.0.0.1:5000
拂玻。 - 在瀏覽器訪問(wèn)
http://127.0.0.0.1:5000
酸些,你就可以進(jìn)行注冊(cè)賬號(hào)宰译,創(chuàng)建文章等操作了,希望一切順利吧魄懂!
最后附上幾張截圖: