Tornado造论坛网站知识点-上

经过前几天的基础知识储备,下面开始真正的实战学习。实战课程知识点较多,不适合笔记记录。

现仅记录一些关键知识点。

  1. 使用WTForms作为参数校验,传递json数据的时候需要使用wtforms_json

    参考文档wtforms_json

    和直接使用tornado的获取参数函数(例如:message_from = MessageForm(self.request.arguments))不同,直接使用json传递数据到form中将会出现遍历数据错误(如字符串只取得第一个字符)。

    使用规则:

    ongimage-20190329180314736

    使用非json方式的时候 我们获得的参数是一个列表形式,不是字符串形式。

1
2
3
4
5
6
7
8
9
10
11
# 在项目初始化的时候 初始化组件
wtforms_json.init()


# 在使用 wtforms 的地方 修改数据获得方式

# 旧的获取数据方式
sms_form = SmsCodeForm(params)

# 新的数据获取方式
sms_form = SmsCodeForm.from_json(params)

原理:

通过猴子补丁将原有的类封装成一个具有from_json方法的新类

  1. 使用 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
    52
    import jwt
    import functools

    from apps.users.models import User


    def authenticated_async(method):
    """在前后端分离的系统中 之前的登录装饰器 authenticated 不再实用"""
    @functools.wraps(method)
    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
    22
    import 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
  1. 通过peeweemodel_to_dict可以将模型直接转为字典

    一般我们在从模型中提取数据返回给前端的时候一个个提取是有点麻烦的。我们可以直接使用peewee提供的model_to_dict将一个模型直接转为字典。

    1
    2
    3
    from playhouse.shortcuts import model_to_dict

    group_dict = model_to_dict(group)

    不过,当在协程中使用的时候,可能会出现一种情况就是模型存在外键关联的时候会报错。

    是因为这个model_to_dict方法是同步的,在协程中不能进行模型间的join操作。

    我们可以在模型中写一个类方法,解决这个问题。

    1
    2
    3
    @classmethod
    def extend(cls):
    return cls.select(cls, User.id, User.nick_name).join(User)
  2. peewee中的多表join和多字段映射同一个model问题

    我们看下下面的model

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class 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
    @classmethod
    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 问题

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!