flask框架中的一些常见问题

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: flask框架中的一些常见问题



前一段flask框架的一个小项目虽然写完了,但是里面有些知识,或遗忘或用的稀里糊涂.对于其中涉及的一些知识点掌握的并不是很透彻,因此在写笔记的时候表述的也不是清晰,今天就来一次大盘点,让我们彻底弄懂这些问题.

我们主要按照下面的思路进行一个分析阐述,大家对于以后的学习也可以借鉴这个思路:

先提一个小点---->你项目里面哪些地方用到了这个知识点----->介绍一下它以及它的优缺点----->你是如何使用的---->用到了其中的哪些知识.



MySQL


项目中凡是涉及到要永久保存的内容我们都用到了MySQL数据库,比如用户的信息(客户的资料可是公司最重要的资料,如果不永久保存,丢了财源,那么就等着倒闭吧,哈哈哈),还有新闻、用户的评论、用户的粉丝(就是关注功能实现的信息)、用户收藏的新闻、用户对评论的点赞、还有新闻的分类(用户界面的首页header部分,还有用户发表新闻的时候所选的分类,管理员在后台审核信息和编辑新闻板式的时候都用到了)等等这些都是用到了MySQL数据库。下面我将后台的数据展示给大家,大家对照着就明白有哪些东西了:

既然这么多地方用到了MySQL这个数据库,那么我们就不能不知道它的相关信息了。MySQL是一款关系型数据库(RDBMS),它使用的是SQL(结构化查询语言)语句进行查询。数据库中的数据都是以表格的形式进行存储的,(元组、记录),(字段、属性)和(指的是我们某一字段设置一个取值范围)前面这三个构成了关系表。

它的特点就是跨平台(管你使用的是Mac、Linux还是Windows,它都支持,你只要想使用就能用,这也就使得它的用户群体很广,因为没有限制)、开源(它的源码是公布的,大家都是可以查看的)、免费(这才是最重要的一点,免费才利于推广,普通用户是富豪的毕竟是少数,免费才是我们选择的第一要素,哈哈哈),然后就是应用范围广。我们在操作数据库的时候一般都是远程,或者在机房里面,哪里有图形界面啊?对着终端,难道安装一个office三件套?不现实吧,正是因为它方便,利于我们远程操作,成为了所有人的最爱,哪哪用的都是它。

项目中我们就用到了扩展Flask-SQLAlchemy,它提供了高层的 ORM(对象关系映射) 和底层的原生数据库的操作。它是一个关系型数据库框架,使用的时候,舍弃了一些性能开销的同时,换来的是开发效率的大大提升。我们直接使用对象来操作数据库就好了,它会帮我们翻译成SQL语句去和MySQL交互,我们就不用记忆SQL语句了,想想就开心。具体的操作可以点击链接查看:flask框架(三)

那么我们就看一下在项目中怎么利用flask-SQLAlchemy使用数据库吧:

1class User(BaseModel, db.Model):
 2    """用户"""
 3    __tablename__ = "info_user"# 指定表的名称
 4
 5    #参数1:表示整数类型,  参数2:表示主键
 6    id = db.Column(db.Integer, primary_key=True)  # 用户编号,设置了主键
 7    nick_name = db.Column(db.String(32), unique=True, nullable=False)  # 用户昵称
 8    password_hash = db.Column(db.String(128), nullable=False)  # 加密的密码
 9    mobile = db.Column(db.String(11), unique=True, nullable=False)  # 手机号
10    avatar_url = db.Column(db.String(256))  # 用户头像路径
11    last_login = db.Column(db.DateTime, default=datetime.now)  # 最后一次登录时间
12    is_admin = db.Column(db.Boolean, default=False)
13    signature = db.Column(db.String(512))  # 用户签名
14    gender = db.Column(  # 订单的状态
15        db.Enum(
16            "MAN",  # 男
17            "WOMAN"  # 女
18        ),
19        default="MAN")
20
21# 初始化 user 模型,并设置数据
22user = User()
23user.nick_name = mobile
24user.mobile = mobile
25
26# 将上面的模型添加到数据库,并进行提交
27db.session.add(user)
28db.session.commit()


上面是网站,用户进行注册的时候,我们需要走的一个大体流程,就是:我们初始化一个用户的模型,也就是创建一个对象user,而SQLAlchemy也就让我们可以像给对象设置属性一样给字段里面添加数据,每个字段的内容都进行了填充,最后将这个对象添加到数据库,然后提交即可。比直接使用数据库用SQL语句要方便的多。



Redis


我们在图片验证码短信验证码session的存储这几个地方使用到了redis数据库。

redis数据库是NOSQL,即非关系型数据库,它是没有外键的,从nosql也可以看出来,它是不支持SQL语句的,刚开始学的时候你也许会觉的头疼,毕竟数据库类型不同,就意味着要记忆更多的语法了。但是当你学习后,你会发现它虽然有很多类型,但是我们常用的语法是没有几句的,而且常用类型的也就一种字符串。具体的操作语句可以点击链接:redis操作命令总结

redis是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。但是这不正与我们用到的session非常的契合吗?session与cookie是一对共存的概念,我们的HTTP默认是无响应的,因为使用了socket套接字,每一次请求完毕之后都会关闭,这样就有一个问题,那就是每次都是全新的访问,大大降低了体验。用session和cookie就可以解决这个问题,浏览器在访问的时候服务器会设置一个cookie发给浏览器,这里面存的是用户的一些信息(比如你浏览过我们网站的哪些东西,你喜欢看哪些文章,方便我给你下次推送,省去你查找的麻烦),然后给服务器存一个session,记录的是一些比较敏感的信息,也是用户的信息(比如用户的用户名、余额、等级、验证码这些东西)。有了这些,我们就可以进行状态保持了,你登陆一个账号,下次访问的时候,只要没有过了session的存储时间,你都不用再次输入账号,这样就很方便了。

redis适用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。说到使用频繁,你想到了什么,那肯定是验证的时候啊,比如我们注册的时候,我们将用户名,密码,验证码等信息一提交,立马就会收到是否注册成功的反馈,这样的效果正是redis存储的一个好处体现。在我们处理验证模块的时候,回想一下过程:

用户一打开注册的弹窗,那么就会有一张图片验证码(这个过程虽然快,但是逻辑操作并不简单。前端会计算一个随机的编码UUID,然后将这个编码发送给后端,后端生成一个验证码图片,生成的时候,有三个值,一个图片的编号,一个是这张图片,一个是图片上的验证码。但是图片的编号我们不使用,我们将前端传过来的UUID作为key,将验证码作为value保存在redis中,然后将图片发送给前端,用户就看到了,因为这个过程很快,所以看不出延迟的,但并不代表编写过程简单)

然后用户填写手机号,图片验证码,点击获取手机验证码,后台收到三个参数(手机号,图片验证码,图片验证码的编号UUID),后台校验参数完整后,看手机号格式是否正确,然后利用UUID从redis中取出验证码和用户输入的验证码进行比对,正确的话就将redis中的图片验证码删掉,然后生成一个随机的短信验证码利用第三方SDK向用户发送短信验证码。后台将用户的手机号作为key,短信验证码作为value保存在redis中。

用户收到之后填写短信验证码,然后点击注册,后台收到三个参数(手机号,短信验证码和用户密码)之后校验短信验证码是否过期,因为redis中保存设置了有效期300s,没有过期的话,验证码校验是否输入正确,正确的话就可以将用户名和密码存储到数据库中了,此处就是MySQL了,因为用户的信息我们要永久保存啊,这就是MySQL的特点了。然后我们给session中保存用户的登录状态,并且返回给前端注册结果。

下面我将项目中上面几种情况用到redis的代码挑拣了出来,大家可以回忆一下:

1import redis
 2
 3# 配置redis
 4redis_store = redis.StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT)
 5
 6# 保存当前生成的图片验证码内容
 7redis_store.setex('ImageCode_' + code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
 8
 9# redis中保存短信验证码内容
10redis_store.set("SMS_" + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
11
12# 保存用户登录状态
13session["user_id"] = user.id
14session["nick_name"] = user.nick_name
15session["mobile"] = user.mobile



请求钩子


我们的项目中,在完善CSRFToken逻辑和拦截普通用户进入管理员页面的时候,用到了请求钩子。

在客户端和服务器交互的过程中,有些准备工作扫尾工作需要处理的时候,为了让每个视图函数避免编写重复的代码,Flask提过了通用设施的功能,这就是请求钩子


请求钩子是通过装饰器的形式实现的,有4种


  1. before_first_request:在处理第一个请求前执行
  2. before_request:在每次请求前执行,在该装饰函数中,一旦return,视图函数不再执行
  1. 接受一个参数:视图函数作出的响应
  2. 在此函数中可以对响应值,在返回之前做最后一步处理,再返回
  1. after_request:如果没有抛出错误,在每次请求后执行
  2. teardown_request:在每次请求后执行
  1. 接受一个参数:用来接收错误信息


但是我们常用的只有2和3两种,在项目中具体的代码展示一下,方便大家进行回忆:

1 #使用请求钩子拦截所有的请求,通过的在cookie中设置csrf_token
 2    @app.after_request
 3    def after_request(resp):
 4        #调用系统方法,获取csrf_token
 5        csrf_token = generate_csrf()
 6
 7        #将csrf_token设置到cookie中
 8        resp.set_cookie("csrf_token",csrf_token)
 9
10        #返回响应
11        return resp
12


1# 使用请求钩子,拦截用户的请求,只有访问了admin_blue,所装饰的视图函数需要拦截
2# 1.拦截的是访问了非登录页面
3# 2.拦截的是普通的用户
4@admin_blue.before_request
5def before_request():
6    if not request.url.endswith("/admin/login"):
7        if not session.get("is_admin"):
8            return redirect("/")


CSRF攻击


什么是csrf攻击?

简单来说就是: 你访问了信任网站A,然后A会用保存你的个人信息并返回给你的浏览器一个cookie,然后呢,在cookie的过期时间之内,你去访问了恶意网站B,它给你返回一些恶意请求代码,要求你去访问网站A,而你的浏览器在收到这个恶意请求之后,在你不知情的情况下,会带上保存在本地浏览器的cookie信息去访问网站A,然后网站A误以为是用户本身的操作,导致来自恶意网站C的攻击代码会被执:发邮件,发消息,修改你的密码,购物,转账,偷窥你的个人信息,导致私人信息泄漏和账户财产安全收到威胁


如何解决?

在psot请求时,form表单或ajax里添加csrf_token(实际项目代码里就是如此简单)


解决原理

添加csrf_token值后,web框架会在响应中自动帮我们生成cookie信息,返回给浏览器,同时在前端代码会生成一个csrf_token值,然后当你post提交信息时,web框架会自动比对cookie里和前端form表单或ajax提交上来的csrf_token值,两者一致,说明是当前浏览器发起的正常请求并处理业务逻辑返回响应,那么第三方网站拿到你的cookie值为什么不能验证通过呢?因为他没你前端的那个随机生成的token值啊,他总不能跑到你电脑面前查看你的浏览器前端页面自动随机生成的token值吧

   

注意:你打开浏览器访问某个url(页面),默认是get请求,也就是说,你只要访问了url,对应的视图函数里只要不是if xx == post的逻辑就会执行,所以你打开页面,他会先生成cookie(token)值,返回给浏览器, 然后你提交表单,或者发ajax请求时,会将浏览器的cookie信息(token值)发送给服务器进行token比对,这个过程相对于你发起了两次请求,第一次是get,第二次才是post,搞清楚这个,你才能明白csrf怎么比对的

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
23天前
|
Python
Flask学习笔记(二):基于Flask框架上传图片到服务器端并原名保存
关于如何使用Flask框架上传图片到服务器端并以其原名保存的教程。
62 1
|
23天前
|
Python
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
这篇博客文章是关于如何使用Flask框架上传特征值数据到服务器端,并将其保存为txt文件的教程。
27 0
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
|
26天前
|
JSON 测试技术 数据库
Python的Flask框架
【10月更文挑战第4天】Python的Flask框架
|
26天前
|
存储 安全 数据库
Flask框架中,如何实现用户身份验证和会话管理?
【10月更文挑战第4天】Flask框架中,如何实现用户身份验证和会话管理?
|
27天前
|
存储 SQL 数据库
使用Python和Flask框架创建Web应用
【10月更文挑战第3天】使用Python和Flask框架创建Web应用
22 1
|
2月前
|
JSON 测试技术 数据库
Python的Flask框架
Python的Flask框架
|
2月前
|
安全 数据安全/隐私保护 Python
基于Flask框架实现一个简易后台用户登录系统
基于Flask框架实现一个简易后台用户登录系统
|
19天前
|
Unix 中间件 数据库
bottle flask 框架对比
Flask:Flask是一个轻量级的Web应用框架, 使用Python编写。
27 0
|
2月前
|
JSON 测试技术 数据库
Python的Flask框架
Python的Flask框架
|
2月前
|
人工智能 安全 数据安全/隐私保护
基于Flask框架实现一个简易后台用户登录系统
基于Flask框架实现一个简易后台用户登录系统