经过前几天的基础知识储备,下面开始真正的实战学习。实战课程知识点较多,不适合笔记记录。
现仅记录一些关键知识点。
使用
WTForms
作为参数校验,传递json
数据的时候需要使用wtforms_json
参考文档wtforms_json
和直接使用
tornado
的获取参数函数(例如:message_from = MessageForm(self.request.arguments)
)不同,直接使用json
传递数据到form
中将会出现遍历数据错误(如字符串只取得第一个字符)。使用规则:
使用非json方式的时候 我们获得的参数是一个列表形式,不是字符串形式。
1 | # 在项目初始化的时候 初始化组件 |
原理:
通过猴子补丁将原有的类封装成一个具有
from_json
方法的新类
使用
PyJWT
来实现 JWT 方式登录前后端分离项目 一般都是 采用JWT方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52import jwt
import functools
from apps.users.models import User
def authenticated_async(method):
"""在前后端分离的系统中 之前的登录装饰器 authenticated 不再实用"""
async def wrapper(self, *args, **kwargs):
# 从请求头中获取到 我们设置的 token
tsessionid = self.request.headers.get("tsessionid", None)
if tsessionid:
# 对token过期进行异常捕捉
try:
# 从 token 中获得我们之前存进 payload 的用户id
send_data = jwt.decode(tsessionid, self.settings["secret_key"], leeway=self.settings["jwt_expire"],
options={"verify_exp": True})
user_id = send_data["id"]
# 从数据库中获取到user并设置给_current_user
try:
user = await self.application.objects.get(User, id=user_id)
# 之所以赋值给 _current_user 是为了使用 self.current_user
# 通过 查看 authenticated 源码 我们知道 current_user 是一个动态属性
self._current_user = user
# 此处需要使用协程方式执行 因为需要装饰的是一个协程
await method(self, *args, **kwargs)
except User.DoesNotExist as e:
self.set_status(401)
self.finish({
"msg": "用户不存在"
})
except jwt.ExpiredSignatureError as e:
self.set_status(401)
self.finish({
"msg": "Token过期"
})
else:
self.set_status(401)
self.finish({
"msg": "请传Token"
})
return wrapper只有上面的还是不行 我们需要在请求头中 新增一个字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import redis
from tornado.web import RequestHandler
# 解决跨域问题
class BaseHandler(RequestHandler):
"""
后续使用前后端分离 我们需要在请求头中加入一个 会话id tsessionid
因此我们需要在 Access-Control-Allow-Headers 增加这个 tsessionid
"""
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Headers', '*')
self.set_header('Access-Control-Max-Age', 1000)
self.set_header('Content-type', 'application/json')
self.set_header('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT, PATCH, OPTIONS')
self.set_header('Access-Control-Allow-Headers',
'Content-Type, tsessionid, Access-Control-Allow-Origin, Access-Control-Allow-Headers, X-Requested-By, Access-Control-Allow-Methods')
def options(self, *args, **kwargs):
pass
通过
peewee
的model_to_dict
可以将模型直接转为字典一般我们在从模型中提取数据返回给前端的时候一个个提取是有点麻烦的。我们可以直接使用
peewee
提供的model_to_dict
将一个模型直接转为字典。1
2
3from playhouse.shortcuts import model_to_dict
group_dict = model_to_dict(group)不过,当在协程中使用的时候,可能会出现一种情况就是模型存在外键关联的时候会报错。
是因为这个
model_to_dict
方法是同步的,在协程中不能进行模型间的join
操作。我们可以在模型中写一个类方法,解决这个问题。
1
2
3
def extend(cls):
return cls.select(cls, User.id, User.nick_name).join(User)peewee
中的多表join
和多字段映射同一个model
问题我们看下下面的
model
1
2
3
4
5
6
7
8
9class PostComment(BaseModel):
# 评论和回复
user = ForeignKeyField(User, verbose_name="用户", related_name="author")
post = ForeignKeyField(Post, verbose_name="帖子")
parent_comment = ForeignKeyField('self', null=True, verbose_name="评论", related_name="comments_parent")
reply_user = ForeignKeyField(User, verbose_name="用户", related_name="replyed_author", null=True)
content = CharField(max_length=1000, verbose_name="内容")
reply_nums = IntegerField(default=0, verbose_name="回复数")
like_nums = IntegerField(default=0, verbose_name="点赞数")上面
model
存在多表关联和多个字段字段映射同一个模型。我们在查询的时候,应该如何解决上面两个问题呢?
1
2
3
4
5
6
7
8
9
10
11
12
def extend(cls):
# 1. 多表join
# 2. 多字段映射同一个model
author = User.alias()
relyed_user = User.alias()
return cls.select(cls, Post, relyed_user.id, relyed_user.nick_name, author.id, author.nick_name).join(
Post, join_type=JOIN.LEFT_OUTER, on=cls.post).switch(cls).join(author, join_type=JOIN.LEFT_OUTER,
on=cls.user).switch(cls).join(
relyed_user, join_type=JOIN.LEFT_OUTER, on=cls.reply_user
)使用别名 解决多字段映射同一个 model
使用 switch 解决 多表 join 问题