1. 程式人生 > >flask學習筆記

flask學習筆記

flask

執行流程

  1. creat一個app物件,通過Flask(name)來註冊方法,然後run_app

  2. 通過flask_script將app註冊到manager 可以自定義一些其他的command manager=Manager(app)

  3. 在creat_app方法中註冊app的一些功能模組

    moment=Monment() moment.init_app(app)

  4. app.register_blueprint() 註冊藍圖:

    auth_blueprint=Blueprint(‘sd’, _ name _)

    app.register_blueprint(auth_blueprint, url_prefix=’/auth’)

  5. 5.

部落格專案

將程式例項的建立推遲到配置檔案的載入後
推遲到工廠函式中

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://[email protected]: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

限制了內容的長度

jinjia

safe 標籤,告訴jinjia 不要轉義html標籤

問題

為什麼在分頁篩選中得不到自己關注自己的那個結果
解決:在follower.html中設定一下。。

評論

一個文章有多個評論。屬於一對多關係
一個使用者能發表多個評論,然後文章可以有多個評論

在user和post中新增relationship和comment關聯

在文章的Index頁面每篇文章下顯示comment數量,
然後點選可以進入檢視post發表文章的主介面,
在主介面下有文章的所有評論,評論的發表時間以及,詳細日期

restful api

  • 因為是無狀態的,又要經過客戶端驗證,所以用http-auth

進行httpie測試

  • 建立文章

    http –auth [email protected]:cat –json POST http://127.0.0.1:5000/api/posts/ “body=I’m adding a post from the command line.”

  • 使用認證令牌來發送請求

    在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 永久的移動了

專案部署 和環境

執行

python hello.py runserver --host 0.0.0.0

連線資料庫:

musql :mysql://username:[email protected]/database

postgresql://username:[email protected]/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 名字可以自己選

heroku create –buildpack heroku/python

新增資料庫

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

DATABASE_URL: postgres://xriuxrfpqtfmpj:f[email protected]ec2-107-21-126-193.compute-1.amazonaws.com:5432/d9uqv1edl13ka8

如果執行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