在 Flask 項目中使用 mongo 的時候,定義模型我喜歡讓框架自動生成 Id 字段,非常方便,不需要自己去已 UUID 或者其他自增形式去生成。一般框架自動生成的 Id 字段類型是 ObjectId,在數(shù)據(jù)庫總中的展示形式是 "id": ObjectId("xxx") 。在查詢時通過 get 都可以輕松地獲取到括號里的字符串,所以我們在傳遞 id 時還是以非常熟悉的字符串格式。
但是最近用上 flask-jwt 插件,碰到一個小坑。Flask-JWT 是實現(xiàn) jwt 的一個框架,也是基于 PyJWt。jwt 即 json web token,之前在實現(xiàn)一些項目時盡管以 restful 的形式釋放出一些接口,但是用到了 session 來保存登錄狀態(tài),并且將 session 信息保存在瀏覽器 cookie 中,每次請求帶上它讓服務(wù)器驗證是否登錄。這就不符合 rest 無狀態(tài)的定義, 真正的 rest 應(yīng)該是每一次請求都是無狀態(tài)的,后端不需要保存狀態(tài)而能夠驗證請求的權(quán)限。這次采用的 jwt 就是把一些自定義的一些信息(比如 id)用密鑰加密變?yōu)橐粋€形式為 xxx.xxx.xxx 的一長串字符串即所謂的 token, 具體這三段如何生成可查看 jwt 官網(wǎng)。在用戶登錄后生成 token 傳給前端,此后每個請求都得在請求頭帶上 token ,這樣服務(wù)器不需要儲存登錄狀態(tài),只是根據(jù)密鑰和加密算法來計算這個 token 是否過期是否被篡改,自然每次請求都是無狀態(tài)的。
在 Flask-JWT 中實現(xiàn)認證方法時之前我這樣寫的:
class Auth():
def authenticate(self, username, password):
...
def identity(self, payload):
user_id = payload['identity']
return User.objects(id=user_id).first()
identity 獲取 id 的過程中發(fā)現(xiàn) id 不是一個可以序列化的變量就會報錯,此時還是得改為字符串類型方便一點。于是我在定義 model 的地方添加 id = db.StringField(required=True, unique=True),自己來通過 UUID 生成,但還是報錯,原來這樣并不能讓系統(tǒng)取消生成 id,官方文檔中說:
Alternatively, you may define one of your own fields to be the document’s
“primary key” by providing primary_key=True as a keyword argument to a >field’s
constructor. Under the hood, MongoEngine will use this field as the id; in >fact
id is actually aliased to your primary key field so you may still
use id to access the primary key if you want:>>> class User(Document): ... email = StringField(primary_key=True) ... name = StringField() ... >>> bob = User(email='bob@example.com', name='Bob') >>> bob.save() >>> bob.id == bob.email == 'bob@example.com' True
primary_key 給誰誰就是 id ,就是這么簡單粗暴,但是為了語言好看我字字段名還是 id,問題解決。
id = db.StringField(primary_key=True)