flask
運行流程
creat一個app對象,通過Flask(name)來注冊方法,然后run_app
通過flask_script將app注冊到manager 可以自定義一些其他的command manager=Manager(app)
-
在creat_app方法中注冊app的一些功能模塊
moment=Monment() moment.init_app(app)
-
app.register_blueprint() 注冊藍圖:
auth_blueprint=Blueprint('sd', _ name _)
app.register_blueprint(auth_blueprint, url_prefix='/auth')
博客項目
將程序實例的創建推遲到配置文件的載入后
推遲到工廠函數中
app=Flask(name) app的名稱
app.config 加載配置文件
使用藍本來處理路由
. 從init.py 中引入
- 避免循環導入依賴的方法 依賴放在最后面
密碼
通過散列來保存密碼
通過設置屬性來調用方法
- 通過使用itsdangerous生成確認令牌
包含用戶id,感覺和散列有點像
必須先提交數據庫,然后再郵件認證
用戶
認證
注冊先存入數據庫,然后根據數據庫返回的序列號生成一個鏈接,email發送,如果點擊鏈接,跳轉到函數,讓數據庫中is_confirmed那項為true
如果沒認證,每次登陸都跳轉到index頁面
- 通過token 來得到data,data是一個字典類型
然后 data.get(id)來得到用戶的序號
_external=True 參數在
url_for()中 的路由的參數是函數的名字
- 通過before login 來確定是否認證,如果沒認證就跳轉到認證頁面
用戶登錄
讓數據庫 User models 增加 userMinxin from flask_login導入的。
- 從view頁面得到當前user的數據庫模型,user.get_or_create(id)
重置密碼:
兩種重置密碼方式
一種是登錄的情況下,一種是非登錄的情況下,都應該郵件確認,保證安全
和郵件認證用戶一個道理,但不同的是,這個token中要包含用戶的信息,那個token是固定的密碼.
所以這個token解密后應該是 包含用戶信息的。
token的有效載荷是固定的,不能被偽造。 有效載荷中包含用戶的信息以及秘鑰的信息
{'reset_password':user_id,'exp':token_expiration}
python2.7用serilize包, 把需要添加的值dump進去
需不需要一個實用的工具包
來處理瑣碎的事情
比如說生成密碼token,發送郵件什么的。
反正發送郵件要交給后臺來處理
pip
安裝到某個文件夾下的txt中
pip install -r requirements/dev.txt
測試
單元測試
文件都寫在tests文件夾下
在manage.py中寫的命令
@manager.command
def test():
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
- python manage.py test
操作權限
關注用戶:1
發表評論:2
寫文章:4
管理評論:8
管理員權限:128
用戶權限
用戶的等級
匿名:0
正式用戶:7
協管員:15
管理員:255
current_user方法
is_anonymous 判斷是否是匿名用戶
藍本
來處理路由,不同的功能使用不同的藍本
相當于django中的分開處理路由
在工廠函數中注冊藍本,相當于django在主頁面的Url添加輔助功能的url
template
template中可以讀取txt路徑,作為html的內容
路徑問題
模板的路徑是相對于程序模板文件夾來說的
base中auth.logout對應的是方法名,而不是路徑名
方法名和路徑名好像必須要一樣。。。
在html中添加動態路徑
路由跳轉
接受函數名作為參數
url_for('main.user',username=current_user.username)
return redirect(url_for('main.user',username=current_user.username))
flask-login
將login_manager添加在login中
LoginManager 對象的 session_protection 屬性可以設為 None、'basic' 或 'strong',以提 供不同的安全等級防止用戶會話遭篡改。設為 'strong' 時,Flask-Login 會記錄客戶端 IP 地址和瀏覽器的用戶代理信息,如果發現異動就登出用戶
表單: user email password remember submit
數據庫
sqlalchemy
用來更新Model表的參數
類型于django migration 和migrate
初始化 創建migrations文件夾
python manage.py db init遷移 相當于django中的migrate
python manage.py db migrate -m 'second migrate'實際遷移到數據庫中 相當于django makemigraions
python hello.py db upgrade
- event sqlalchemy set 時間的監聽程序,只要進行了set 就會調用on_changed_body函數
on_change_body函數將markdown渲染為html,然后返回
聯結操作
在文章頁面只顯示用戶關注用戶的文章
Post.join(Follow,Follow.follow_id==Post.author.id)
相關知識
主鍵是不唯一的
casacde 級聯刪除的意思,當刪除外鍵依賴的主表時,子表內容自動刪除
api
- order_by.all()
- filter_by().first()
- 對主鍵進行查找 .get_or_404()
分頁
- query.order_by(Post.timestamp.desc()).paginaton()
分頁的屬性: - items 查詢到的記錄
- query 源查詢
- page 當前頁數
- prev_num 上一頁的頁數
- next_num 下一頁的頁數
- has_next 是否有上一頁
-has_prev 是否有上一頁 - pages 總頁數
- per_page 每頁的記錄數量
- total 記錄總數、
在對象上調用的方法
iter_pages 迭代器,返回一個頁數列表
寫好一個分頁頁面, _macros,然后在其他需要分頁的html中將這個頁面插入
alembic
- 初始化
mbic init YOUR_ALEMBIC_DIR
relationship
關系之間的便捷調用
Student model:
classes=db.relationship('class',secondary=restrations,backerf=db.backref('students',lazy='dynamic'),
lazy='dynamic')
student的clsses屬性和class表建立relationship,
同時提供一個反向聲明, class表中的對象可以直接classe.students來篩選出選了該課的學生
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()
多對多的關系
兩個表對一個輔助表建立外鍵索引,然后根據約束來查找
比如:
class 和student 再建立一張表:sdu_class, 表中的屬分別和兩個子表建立索引關系
具體實現
通過 兩個表建立relationship 同時在relationship中聲明secondary實現many to many
自引用
上面的學生和老師有兩個實體
如果是用戶和用戶之間,那么只有一個關系。
一個實體之間的多對多稱為自引用
給用戶添加兩個屬性
follower和followed
要額外儲存兩個實體間的額外信息,比如關注者的關注日期
建立一個新的表,同時在user中添加,followed和follwer 和follow表建立自引用
lazy
lazy決定了什么時候從數據庫中加載數據
lazy的三個參數
dynamic joined select
dynamic 應該是relationship被調用時返回一個query()對象
joined relationship被調用時返回兩個表join的結果值
select 是訪問到屬性時,會加載全部的屬性。
backref
第一個字段是表的名稱,第二個字段是給第一個字段表中調用的別名
post=db.relationship('Post',backref='author',lazy='dynamic)
backref提供反向聲明
user和post形成一對多的關系,一個user可以生產多個post
Post在找user時可以根據author直接找到,而不需要再根據author_id在User中查找
back_populates
和backref的不同點,在于back_populates是顯式聲明了關系的應用
版本遷移 遷移腳本中的函數
- current 查看當前版本的信息
- upgrade upgrade() 函數把遷移中的改動應用到數據庫中
- downgrade() 函數則將改動 刪除。
回退
數據庫根據 migrations 回退的方法
downgrade后刪除一個版本的函數就好
- 增加用戶
db.session.add(user)
db shell
from app.models import *
Role.insert_roles()
date
date.utcnow 沒有括號,default可以接受函數作為參數
連接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)方法中,檢查當前用戶是否關注了user用戶,應該用self.followed.filter_by(follower=user)
self.followed為current_user關注的所有人的集合,在這個集合中,所有的followed項都為current_user,所有的follower項為各個被關注的用戶,所以要找出是否關注了某個用戶,應該是,
self.followed.filter_by(follower=user)
View
bootstrap
<div class="col-md-4">
限制了內容的長度
jinjia
safe 標簽,告訴jinjia 不要轉義html標簽
問題
為什么在分頁篩選中得不到自己關注自己的那個結果
解決:在follower.html中設置一下。。
評論
一個文章有多個評論。屬于一對多關系
一個用戶能發表多個評論,然后文章可以有多個評論
在user和post中添加relationship和comment關聯
在文章的Index頁面每篇文章下顯示comment數量,
然后點擊可以進入查看post發表文章的主界面,
在主界面下有文章的所有評論,評論的發表時間以及,詳細日期
restful api
- 因為是無狀態的,又要經過客戶端驗證,所以用http-auth
進行httpie測試
http --json --auth fjl2401@163.com:cat GET http://127.0.0.1:5000/api/posts
-
創建文章
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."
-
使用認證令牌來發送請求
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那里
所以在請求api時會出現錯誤是因為在初始化時沒有加入before_request()
http狀態碼
200 響應成功
201 創建成功
202 接受
301 Moved Permanently 永久的移動了
項目部署 和環境
https://icecola.herokuapp.com/ | https://git.heroku.com/icecola.git
運行
python hello.py runserver --host 0.0.0.0
連接數據庫:
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 來部署gunicorn
supervisor 進程管理工具
~/django_1/VENV_flask/hello_flask
利用supervisor來監視進程,在進程死亡后拉起
sudo mkdir /etc/supervisor
sudo echo_supervisord_conf > /etc/supervisord.conf 生成配置文件
查看默認配置文件
查看supervisord是否在運行
ps aux | grep supervisord
echo_supervisord_conf
- 啟動服務
supervisord -c /etc/supervisor/supervisord.conf
運行自己寫的配置文件:
supervisord -c /etc/supervisor/conf.d/hello_flask.conf
使用 supervisorctl
Supervisorctl 是 supervisord 的一個命令行客戶端工具,啟動時需要指定與 supervisord 使用同一份配置文件,否則與 supervisord 一樣按照順序查找配置文件。
進入客戶端
supervisorctl -c /etc/supervisord.conf
supervisorctl -c /etc/supervisor/conf.d/hello_flask.conf
-
進入之后
- status 查看狀態
- reread 重新啟動
- restart program_name 重啟某個項目
- stop all 停止所有
- update 更新
-
啟動某個進程
supervisorctl starprogram_name
重新加載
supervisorctl reload
配置了http服務后的啟動 sudo supervisorctl -u chris -p 123
-
gunicorn直接啟動命令
/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報錯:
關聯到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
輸入在官網注冊的賬戶和 密碼
創建應用
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
添加數據庫
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和網頁地址
git remote show heroku
進入數據庫
heroku pg:psql -a floating-ravine-41608
如果運行git push heroku出錯,運行這步
heroku git:remote -a floating-ravine-41608
push上去 即可運行
git push heroku master
部署flask 需要的文件
runtime.txt 表示運行時的python環境
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和 運行的服務器
web: gunicorn routes:app
.env.env 配置環境變量 ,heroku會直接從這里讀取
FLASK_APP=flasky.py
FLASK_CONFIG=heroku
MAIL_USERNAME=fjl2401
MAIL_PASSWORD=youpass
添加后臺任務
在views中用current_user來調用,實現跳轉到models的方法
用rq來處理,在models.user中定義launch方法,實現跳轉到current_app的隊列
然后通過rq_job這個名字自動加入到消息隊列中,任務的名稱為name+user.id
在app的init中 任務隊列初始化,連接到redis服務器
在當前目錄下找tasks這個文件,然后根據 views傳進來的名稱來定位到對于的目錄下的py文件的函數名,
運行對應的函數。
在指定name任務隊列中分配worker,保證該任務隊列有worker
然后實例化一個隊列對象
queue = rq.Queue('new_work', connection=Redis.from_url('redis://'))
將方法添加到隊列生成job
job = queue.enqueue('app.tasks.example', 23)
rabbit-rmq
地址
用戶名和密碼 guest
啟動: ./rabbitmq-server
ps -ef|grep rabbit