flask
運(yùn)行流程
creat一個(gè)app對(duì)象,通過(guò)Flask(name)來(lái)注冊(cè)方法,然后run_app
通過(guò)flask_script將app注冊(cè)到manager 可以自定義一些其他的command manager=Manager(app)
-
在creat_app方法中注冊(cè)app的一些功能模塊
moment=Monment() moment.init_app(app)
-
app.register_blueprint() 注冊(cè)藍(lán)圖:
auth_blueprint=Blueprint('sd', _ name _)
app.register_blueprint(auth_blueprint, url_prefix='/auth')
博客項(xiàng)目
將程序?qū)嵗膭?chuàng)建推遲到配置文件的載入后
推遲到工廠函數(shù)中
app=Flask(name) app的名稱
app.config 加載配置文件
使用藍(lán)本來(lái)處理路由
. 從init.py 中引入
- 避免循環(huán)導(dǎo)入依賴的方法 依賴放在最后面
密碼
通過(guò)散列來(lái)保存密碼
通過(guò)設(shè)置屬性來(lái)調(diào)用方法
- 通過(guò)使用itsdangerous生成確認(rèn)令牌
包含用戶id,感覺和散列有點(diǎn)像
必須先提交數(shù)據(jù)庫(kù),然后再郵件認(rèn)證
用戶
認(rèn)證
注冊(cè)先存入數(shù)據(jù)庫(kù)微王,然后根據(jù)數(shù)據(jù)庫(kù)返回的序列號(hào)生成一個(gè)鏈接,email發(fā)送靡努,如果點(diǎn)擊鏈接,跳轉(zhuǎn)到函數(shù)蹬蚁,讓數(shù)據(jù)庫(kù)中is_confirmed那項(xiàng)為true
如果沒認(rèn)證砰盐,每次登陸都跳轉(zhuǎn)到index頁(yè)面
- 通過(guò)token 來(lái)得到data,data是一個(gè)字典類型
然后 data.get(id)來(lái)得到用戶的序號(hào)
_external=True 參數(shù)在
url_for()中 的路由的參數(shù)是函數(shù)的名字
- 通過(guò)before login 來(lái)確定是否認(rèn)證致盟,如果沒認(rèn)證就跳轉(zhuǎn)到認(rèn)證頁(yè)面
用戶登錄
讓數(shù)據(jù)庫(kù) User models 增加 userMinxin from flask_login導(dǎo)入的。
- 從view頁(yè)面得到當(dāng)前user的數(shù)據(jù)庫(kù)模型尤慰,user.get_or_create(id)
重置密碼:
兩種重置密碼方式
一種是登錄的情況下馏锡,一種是非登錄的情況下,都應(yīng)該郵件確認(rèn)伟端,保證安全
和郵件認(rèn)證用戶一個(gè)道理杯道,但不同的是,這個(gè)token中要包含用戶的信息责蝠,那個(gè)token是固定的密碼.
所以這個(gè)token解密后應(yīng)該是 包含用戶信息的党巾。
token的有效載荷是固定的,不能被偽造玛歌。 有效載荷中包含用戶的信息以及秘鑰的信息
{'reset_password':user_id昧港,'exp':token_expiration}
python2.7用serilize包擎椰, 把需要添加的值dump進(jìn)去
需不需要一個(gè)實(shí)用的工具包
來(lái)處理瑣碎的事情
比如說(shuō)生成密碼token支子,發(fā)送郵件什么的。
反正發(fā)送郵件要交給后臺(tái)來(lái)處理
pip
安裝到某個(gè)文件夾下的txt中
pip install -r requirements/dev.txt
測(cè)試
單元測(cè)試
文件都寫在tests文件夾下
在manage.py中寫的命令
@manager.command
def test():
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
- python manage.py test
操作權(quán)限
關(guān)注用戶:1
發(fā)表評(píng)論:2
寫文章:4
管理評(píng)論:8
管理員權(quán)限:128
用戶權(quán)限
用戶的等級(jí)
匿名:0
正式用戶:7
協(xié)管員:15
管理員:255
current_user方法
is_anonymous 判斷是否是匿名用戶
藍(lán)本
來(lái)處理路由达舒,不同的功能使用不同的藍(lán)本
相當(dāng)于django中的分開處理路由
在工廠函數(shù)中注冊(cè)藍(lán)本值朋,相當(dāng)于django在主頁(yè)面的Url添加輔助功能的url
template
template中可以讀取txt路徑,作為html的內(nèi)容
路徑問題
模板的路徑是相對(duì)于程序模板文件夾來(lái)說(shuō)的
base中auth.logout對(duì)應(yīng)的是方法名巩搏,而不是路徑名
方法名和路徑名好像必須要一樣昨登。。贯底。
在html中添加動(dòng)態(tài)路徑
路由跳轉(zhuǎn)
接受函數(shù)名作為參數(shù)
url_for('main.user',username=current_user.username)
return redirect(url_for('main.user',username=current_user.username))
flask-login
將login_manager添加在login中
LoginManager 對(duì)象的 session_protection 屬性可以設(shè)為 None丰辣、'basic' 或 'strong'撒强,以提 供不同的安全等級(jí)防止用戶會(huì)話遭篡改。設(shè)為 'strong' 時(shí)笙什,F(xiàn)lask-Login 會(huì)記錄客戶端 IP 地址和瀏覽器的用戶代理信息飘哨,如果發(fā)現(xiàn)異動(dòng)就登出用戶
表單: user email password remember submit
數(shù)據(jù)庫(kù)
sqlalchemy
用來(lái)更新Model表的參數(shù)
類型于django migration 和migrate
初始化 創(chuàng)建migrations文件夾
python manage.py db init遷移 相當(dāng)于django中的migrate
python manage.py db migrate -m 'second migrate'實(shí)際遷移到數(shù)據(jù)庫(kù)中 相當(dāng)于django makemigraions
python hello.py db upgrade
- event sqlalchemy set 時(shí)間的監(jiān)聽程序,只要進(jìn)行了set 就會(huì)調(diào)用on_changed_body函數(shù)
on_change_body函數(shù)將markdown渲染為html琐凭,然后返回
聯(lián)結(jié)操作
在文章頁(yè)面只顯示用戶關(guān)注用戶的文章
Post.join(Follow,Follow.follow_id==Post.author.id)
相關(guān)知識(shí)
主鍵是不唯一的
casacde 級(jí)聯(lián)刪除的意思芽隆,當(dāng)刪除外鍵依賴的主表時(shí),子表內(nèi)容自動(dòng)刪除
api
- order_by.all()
- filter_by().first()
- 對(duì)主鍵進(jìn)行查找 .get_or_404()
分頁(yè)
- query.order_by(Post.timestamp.desc()).paginaton()
分頁(yè)的屬性: - items 查詢到的記錄
- query 源查詢
- page 當(dāng)前頁(yè)數(shù)
- prev_num 上一頁(yè)的頁(yè)數(shù)
- next_num 下一頁(yè)的頁(yè)數(shù)
- has_next 是否有上一頁(yè)
-has_prev 是否有上一頁(yè) - pages 總頁(yè)數(shù)
- per_page 每頁(yè)的記錄數(shù)量
- total 記錄總數(shù)统屈、
在對(duì)象上調(diào)用的方法
iter_pages 迭代器胚吁,返回一個(gè)頁(yè)數(shù)列表
寫好一個(gè)分頁(yè)頁(yè)面, _macros愁憔,然后在其他需要分頁(yè)的html中將這個(gè)頁(yè)面插入
alembic
- 初始化
mbic init YOUR_ALEMBIC_DIR
relationship
關(guān)系之間的便捷調(diào)用
Student model:
classes=db.relationship('class',secondary=restrations,backerf=db.backref('students',lazy='dynamic'),
lazy='dynamic')
student的clsses屬性和class表建立relationship腕扶,
同時(shí)提供一個(gè)反向聲明, class表中的對(duì)象可以直接classe.students來(lái)篩選出選了該課的學(xué)生
flask中session操作
- 增加 和更新
db.session.add(user)
db.session.commit()
-
刪除
db.session.delete(user)
db.session.commit()
查詢方式
主鍵查詢
receive_user=User.query.filter_by(username=recepient).first_or_404()
receive_user=User.query.get_or_404(id)
其他條件查詢:
receive_user=User.query.filter_by(username=recepient).first()
多對(duì)多的關(guān)系
兩個(gè)表對(duì)一個(gè)輔助表建立外鍵索引吨掌,然后根據(jù)約束來(lái)查找
比如:
class 和student 再建立一張表:sdu_class蕉毯, 表中的屬分別和兩個(gè)子表建立索引關(guān)系
具體實(shí)現(xiàn)
通過(guò) 兩個(gè)表建立relationship 同時(shí)在relationship中聲明secondary實(shí)現(xiàn)many to many
自引用
上面的學(xué)生和老師有兩個(gè)實(shí)體
如果是用戶和用戶之間,那么只有一個(gè)關(guān)系思犁。
一個(gè)實(shí)體之間的多對(duì)多稱為自引用
給用戶添加兩個(gè)屬性
follower和followed
要額外儲(chǔ)存兩個(gè)實(shí)體間的額外信息代虾,比如關(guān)注者的關(guān)注日期
建立一個(gè)新的表,同時(shí)在user中添加激蹲,followed和follwer 和follow表建立自引用
lazy
lazy決定了什么時(shí)候從數(shù)據(jù)庫(kù)中加載數(shù)據(jù)
lazy的三個(gè)參數(shù)
dynamic joined select
dynamic 應(yīng)該是relationship被調(diào)用時(shí)返回一個(gè)query()對(duì)象
joined relationship被調(diào)用時(shí)返回兩個(gè)表join的結(jié)果值
select 是訪問到屬性時(shí)棉磨,會(huì)加載全部的屬性。
backref
第一個(gè)字段是表的名稱,第二個(gè)字段是給第一個(gè)字段表中調(diào)用的別名
post=db.relationship('Post',backref='author',lazy='dynamic)
backref提供反向聲明
user和post形成一對(duì)多的關(guān)系学辱,一個(gè)user可以生產(chǎn)多個(gè)post
Post在找user時(shí)可以根據(jù)author直接找到乘瓤,而不需要再根據(jù)author_id在User中查找
back_populates
和backref的不同點(diǎn),在于back_populates是顯式聲明了關(guān)系的應(yīng)用
版本遷移 遷移腳本中的函數(shù)
- current 查看當(dāng)前版本的信息
- upgrade upgrade() 函數(shù)把遷移中的改動(dòng)應(yīng)用到數(shù)據(jù)庫(kù)中
- downgrade() 函數(shù)則將改動(dòng) 刪除策泣。
回退
數(shù)據(jù)庫(kù)根據(jù) migrations 回退的方法
downgrade后刪除一個(gè)版本的函數(shù)就好
- 增加用戶
db.session.add(user)
db shell
from app.models import *
Role.insert_roles()
date
date.utcnow 沒有括號(hào)咏尝,default可以接受函數(shù)作為參數(shù)
連接mysql
dir=mysql://root@localhost:3306/hello_flask?charset=utf8mb4
解決bug
配置名必須大寫...
TypeError: init() takes at most 2 arguments (3 given) form表格中沒有添加路徑
書中的bug
在User model中的is_following(user)方法中,檢查當(dāng)前用戶是否關(guān)注了user用戶弛针,應(yīng)該用self.followed.filter_by(follower=user)
self.followed為current_user關(guān)注的所有人的集合冀宴,在這個(gè)集合中,所有的followed項(xiàng)都為current_user,所有的follower項(xiàng)為各個(gè)被關(guān)注的用戶危队,所以要找出是否關(guān)注了某個(gè)用戶聪建,應(yīng)該是,
self.followed.filter_by(follower=user)
View
bootstrap
<div class="col-md-4">
限制了內(nèi)容的長(zhǎng)度
jinjia
safe 標(biāo)簽茫陆,告訴jinjia 不要轉(zhuǎn)義html標(biāo)簽
問題
為什么在分頁(yè)篩選中得不到自己關(guān)注自己的那個(gè)結(jié)果
解決:在follower.html中設(shè)置一下金麸。。
評(píng)論
一個(gè)文章有多個(gè)評(píng)論簿盅。屬于一對(duì)多關(guān)系
一個(gè)用戶能發(fā)表多個(gè)評(píng)論挥下,然后文章可以有多個(gè)評(píng)論
在user和post中添加relationship和comment關(guān)聯(lián)
在文章的Index頁(yè)面每篇文章下顯示comment數(shù)量揍魂,
然后點(diǎn)擊可以進(jìn)入查看post發(fā)表文章的主界面,
在主界面下有文章的所有評(píng)論棚瘟,評(píng)論的發(fā)表時(shí)間以及愉烙,詳細(xì)日期
restful api
- 因?yàn)槭菬o(wú)狀態(tài)的,又要經(jīng)過(guò)客戶端驗(yàn)證解取,所以用http-auth
進(jìn)行httpie測(cè)試
http --json --auth fjl2401@163.com:cat GET http://127.0.0.1:5000/api/posts
-
創(chuàng)建文章
http --auth fjl2401@163.com:cat --json POST http://127.0.0.1:5000/api/posts/ "body=I'm adding a post from the command line."
-
使用認(rèn)證令牌來(lái)發(fā)送請(qǐng)求
http --auth fjl2401@163.com:cat --json GET http://127.0.0.1:5000/api/v1.0/token
在model中的生成令牌的方法:
s = Serializer(current_app.config['SECRET_KEY'],expires_in=expiration)return s.dumps({'id': self.id}).decode('ascii')
http --json --auth eyJhbGciOiJIUzI1NiIsImV4cCI6MTUyNjk1MjM4MCwiaWF0IjoxNTI2OTQ4NzgwfQ.eyJpZCI6MX0.K97b2cUxBU4cqCI63IwFCgzYpMVMre7BRfqXP-6yVmU: GET http://127.0.0.1:5000/api/v1.0/posts/
g.current_user
g.current_user的初始化在before_request那里
所以在請(qǐng)求api時(shí)會(huì)出現(xiàn)錯(cuò)誤是因?yàn)樵诔跏蓟瘯r(shí)沒有加入before_request()
http狀態(tài)碼
200 響應(yīng)成功
201 創(chuàng)建成功
202 接受
301 Moved Permanently 永久的移動(dòng)了
項(xiàng)目部署 和環(huán)境
https://icecola.herokuapp.com/ | https://git.heroku.com/icecola.git
運(yùn)行
python hello.py runserver --host 0.0.0.0
連接數(shù)據(jù)庫(kù):
musql :mysql://username:password@localhost/database
postgresql://username:password@localhost/database
sqlite : sqlite:///absolute/path/to/databse
部署gunicorn
將flask部署在5001端口上
gunicorn -b 0.0.0.0:5001 -w 4 manage:app
gunicorn -b localhost:5000 -w 4 hello_flask:app
supervisor 來(lái)部署gunicorn
supervisor 進(jìn)程管理工具
~/django_1/VENV_flask/hello_flask
利用supervisor來(lái)監(jiān)視進(jìn)程步责,在進(jìn)程死亡后拉起
sudo mkdir /etc/supervisor
sudo echo_supervisord_conf > /etc/supervisord.conf 生成配置文件
查看默認(rèn)配置文件
查看supervisord是否在運(yùn)行
ps aux | grep supervisord
echo_supervisord_conf
- 啟動(dòng)服務(wù)
supervisord -c /etc/supervisor/supervisord.conf
運(yùn)行自己寫的配置文件:
supervisord -c /etc/supervisor/conf.d/hello_flask.conf
使用 supervisorctl
Supervisorctl 是 supervisord 的一個(gè)命令行客戶端工具,啟動(dòng)時(shí)需要指定與 supervisord 使用同一份配置文件禀苦,否則與 supervisord 一樣按照順序查找配置文件蔓肯。
進(jìn)入客戶端
supervisorctl -c /etc/supervisord.conf
supervisorctl -c /etc/supervisor/conf.d/hello_flask.conf
-
進(jìn)入之后
- status 查看狀態(tài)
- reread 重新啟動(dòng)
- restart program_name 重啟某個(gè)項(xiàng)目
- stop all 停止所有
- update 更新
-
啟動(dòng)某個(gè)進(jìn)程
supervisorctl starprogram_name
重新加載
supervisorctl reload
配置了http服務(wù)后的啟動(dòng) sudo supervisorctl -u chris -p 123
-
gunicorn直接啟動(dòng)命令
/home/ubuntu/django_1/VENV_flask/bin/gunicorn -b localhost:5000 -w 4 hello_flask:app
?conf文件的配置
[program:hello_flask ]
command=/home/ubuntu/django_1/VENV_flask/bin/gunicorn -b localhost:5000 -w 4 hello_flask:app
directory=/home/ubuntu/django_1/VENV_flask/hello_flask
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stdout_logfile=/home/ubuntu/django_1/VENV_flask/hello_flask/log/out_log.log
stderr_logfile=/home/ubuntu/django_1/VENV_flask/hello_flask/log/err_log.log
[supervisord]
575報(bào)錯(cuò):
關(guān)聯(lián)到nginx
location {
# forward application requests to the gunicorn server
proxy_pass http://localhost:5000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
# handle static files directly, without forwarding to the application
alias /home/ubuntu/django_1/VENV_flask/hello_flask/app/static;
expires 30d;
}
# write access and error logs to /var/log
access_log /var/log/hello_flask_access.log;
error_log /var/log/hello_flask_error.log;
}
部署到heroku
登錄
heroku login
輸入在官網(wǎng)注冊(cè)的賬戶和 密碼
創(chuàng)建應(yīng)用
heroku create <appname> 名字可以自己選
heroku create --buildpack heroku/python
Creating heroku-postgresql on ? floating-ravine-41608... free
https://floating-ravine-41608.herokuapp.com/ | https://git.heroku.com/floating-ravine-41608.git
添加數(shù)據(jù)庫(kù)
heroku addons:create heroku-postgresql -a floating-ravine-41608
Database has been created and is available
! This database is empty. If upgrading, you can transfer
! data from another database with pg:copy
Created postgresql-parallel-90394 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation
顯示git和網(wǎng)頁(yè)地址
git remote show heroku
進(jìn)入數(shù)據(jù)庫(kù)
heroku pg:psql -a floating-ravine-41608
如果運(yùn)行g(shù)it push heroku出錯(cuò),運(yùn)行這步
heroku git:remote -a floating-ravine-41608
push上去 即可運(yùn)行
git push heroku master
部署flask 需要的文件
runtime.txt 表示運(yùn)行時(shí)的python環(huán)境
python-2.7.14
requirements.txt 用 pip freeze > requirements.txt 生成
Flask==0.10.1
Flask-SQLAlchemy==2.0
Flask-WTF==0.12
Jinja2==2.8
MarkupSafe==0.23
SQLAlchemy==1.0.8
WTForms==2.0.2
Werkzeug==0.10.4
click==4.1
decorator==4.0.2
geocoder==1.4.1
gunicorn==19.3.0
itsdangerous==0.24
psycopg2==2.7.3.1
ratelim==0.1.6
requests==2.7.0
six==1.9.0
wsgiref==0.1.2
Procfile 顯示路由的app和 運(yùn)行的服務(wù)器
web: gunicorn routes:app
.env.env 配置環(huán)境變量 ,heroku會(huì)直接從這里讀取
FLASK_APP=flasky.py
FLASK_CONFIG=heroku
MAIL_USERNAME=fjl2401
MAIL_PASSWORD=youpass
添加后臺(tái)任務(wù)
在views中用current_user來(lái)調(diào)用振乏,實(shí)現(xiàn)跳轉(zhuǎn)到models的方法
用rq來(lái)處理蔗包,在models.user中定義launch方法,實(shí)現(xiàn)跳轉(zhuǎn)到current_app的隊(duì)列
然后通過(guò)rq_job這個(gè)名字自動(dòng)加入到消息隊(duì)列中慧邮,任務(wù)的名稱為name+user.id
在app的init中 任務(wù)隊(duì)列初始化调限,連接到redis服務(wù)器
在當(dāng)前目錄下找tasks這個(gè)文件,然后根據(jù) views傳進(jìn)來(lái)的名稱來(lái)定位到對(duì)于的目錄下的py文件的函數(shù)名误澳,
運(yùn)行對(duì)應(yīng)的函數(shù)耻矮。
在指定name任務(wù)隊(duì)列中分配worker,保證該任務(wù)隊(duì)列有worker
然后實(shí)例化一個(gè)隊(duì)列對(duì)象
queue = rq.Queue('new_work', connection=Redis.from_url('redis://'))
將方法添加到隊(duì)列生成job
job = queue.enqueue('app.tasks.example', 23)
rabbit-rmq
地址
用戶名和密碼 guest
啟動(dòng): ./rabbitmq-server
ps -ef|grep rabbit