在 Flask
项目中使用 mongo
的时候,定义模型我喜欢让框架自动生成 Id
字段,非常方便,不需要自己去已 UUID 或者其他自增形式去生成。一般框架自动生成的 Id
字段类型是 ObjectId
,在数据库总中的展示形式是 "id": ObjectId("xxx")
。在查询时通过 get 都可以轻松地获取到括号里的字符串,所以我们在传递 id 时还是以非常熟悉的字符串格式。
但是最近用上 flask-jwt
插件,碰到一个小坑。Flask-JWT
是实现 jwt
的一个框架,也是基于 PyJWt
。jwt
即 json web token
,之前在实现一些项目时尽管以 restful
的形式释放出一些接口,但是用到了 session
来保存登录状态,并且将 session
信息保存在浏览器 cookie
中,每次请求带上它让服务器验证是否登录。这就不符合 rest
无状态的定义, 真正的 rest
应该是每一次请求都是无状态的,后端不需要保存状态而能够验证请求的权限。这次采用的 jwt
就是把一些自定义的一些信息(比如 id)用密钥加密变为一个形式为 xxx.xxx.xxx
的一长串字符串即所谓的 token
, 具体这三段如何生成可查看 jwt
官网。在用户登录后生成 token
传给前端,此后每个请求都得在请求头带上 token
,这样服务器不需要储存登录状态,只是根据密钥和加密算法来计算这个 token
是否过期是否被篡改,自然每次请求都是无状态的。
在 Flask-JWT
中实现认证方法时之前我这样写的:
class Auth():
def authenticate(self, username, password):
...
def identity(self, payload):
user_id = payload['identity']
return User.objects(id=user_id).first()
identity
获取 id
的过程中发现 id
不是一个可以序列化的变量就会报错,此时还是得改为字符串类型方便一点。于是我在定义 model
的地方添加 id = db.StringField(required=True, unique=True)
,自己来通过 UUID 生成,但还是报错,原来这样并不能让系统取消生成 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)