9 图片社交网站
本章新涉及的Python包:Flask-Dropzone, Pillow, Flask-Avatars, Whoosh, Flask-Whooshee。
9.1 项目组织架构
1、功能式架构
在该架构中,程序包由各个代表程序组件(功能)的子包组成,如 blueprints(蓝本),froms(表单),templates(模板),models(模型)等。这也是前一章个人博客项目中,所采用的架构方式。
2、分区式架构
在分区式架构中,程序被按照自身的板块分成不同的子包,如 front(前台),auth(用户),dashboard(后台)。这种分类自然决定了每一个子包都对应着一个蓝本。
myapp/ dashboard/ - __init__.py - views.py - forms.py templates/ static/ front/ ... auth/ ... __init__.py
也许上面两种架构的描述会让你有些混淆,你也可以这样理解:功能式架构将相似的代码组织在一起,而分区式架构将同一块业务组织在一起。
3、混合式架构
即不按照常规的分类来组织。比如在分区式架构的基础上,使各个蓝本公用程序包根目录下的模板文件夹和静态文件夹。
Flask并不限制你组织项目的方式,你可以根据自己程序的特点自由选择。
9.2 编写程序骨架
1、可能用到的资源
- 在线图片占位服务:如基于Unspalsh的Lorem Picsum。在程序开发的过程种,可以生成不同的用于占位的图片。
- 开源图标集:如Font Awesome,Material Design Icons,Octions等,还有与Bootstrap集成良好的Iconic。(p308)
2、缓存破坏
如果你总是向同一个url发起请求,而实际获取不同的返回结果。如
<img src="https://picsum.photos/800/?random">
但浏览器可能会使用缓存的响应。为了处理这个问题,你可以再加一个无意义但是会变化的查询字符串。
9.3 高级用户认证
注册一个账号通常包含这样的步骤:填写注册信息、接收注册邮件、通过单击验证链接来确认账号。
1、验证邮箱地址
验证链接是一个类似/confirm/的URL,后面是一个传递给视图函数的变量,即一个用来验证用户身份的令牌。用户点击链接使服务器收到请求后,会解析令牌以获得存储再其中的用户 id,然后执行确认的操作。
因为令牌经过签名,所以可以确保其不会被篡改。
问题:“签名”是原理是什么?
- 扩展Flask-Security:可以简化用户认证的实现过程。
9.4 基于用户角色的权限管理
在一些简单的程序中,通常只有两种用户角色:普通用户和管理员,所以在角色和权限上并不需要花费太多的功夫。但在一些复杂的程序中,你可能会有更丰富的角色,如被封禁的用户、被锁定的用户等等。每一种角色又有着不同的权限,角色和权限之间是多对多的关系。
roles_permissions_map = { 'Locked': ['Follow', 'Collect'], 'User': ['Follow', 'Collect', 'Comment'], ... }
在程序开始时,你需要提供一个方法初始化角色和权限的关系,即将该关系写入数据库中。然后,你可以编写权限验证装饰器,附加到视图函数前,使具有响应权限的用户才能访问该视图函数。
9.5 使用Flask-Dropzone优化文件上传
使用普通表单进行文件上传时,如果同时上传多个文件,用户只能在一个毫无变化的页面面前苦苦等待。为了优化用户体验,我们可以显示一个包含上传文件的列表,展示文件上传的进度。Flask-Dropzone扩展可以帮助我们做到这件事情。
Flask-Dropzone提供了基本配置、内置的文件类型配置、以及错误消息的配置。(p326)
该扩展还内置了对于CSRFProtect扩展的支持,可以在上传文件表单中自动添加csrf令牌隐藏字段,并在处理文件上传请求时自动验证csrf令牌。
DROPZONE_ENABLE_CSRF = True
载。有可以分别使用dropzone.load_css()和dropzone.load_js()方法从CDN加载静态资源。
保存图片
Flask-dropzone扩展承担了渲染界面、接收文件和验证工作,而保存图片需要我们自己完成。图片的保存包含两个更具体的操作:
将图片文件保存到文件系统
- 将图片信息保存到数据库
Flask-Dropzone还扩展内置了一个生成随机文件名的函数:
from flask_dropzone import random_filename filename = random_filename(f.filename) # 从原文件名生成新的文件名
Dropzone.js(Flask-Dropzone的静态资源)通过AJAX请求发送文件,每个文件一个请求,因此在完成上传后并不会将网页的控制权交给Flask的视图函数。一个不错的做法是提供一个按钮,让用户自行控制。你也可以配置键DROPZONE-DEDIRECT_VIEW在完成上传后自动跳转到指定页面。
图片裁剪
网页中大量体积较大的图片会延缓网页的加载速度。一种可行的做法是,为一张图片保存同时保存多个不同大小的副本,在不同的场景下,使用不同大小的图片。(p333)
9.6 使用Flask-Avatars处理用户头像
在用户还没有自定义头像时,可以为用户提供一个默认头像。Flask-Avatars在模板中开放了avatars类:
<img src="{{ avatars.defualt() }}">
默认头像太过简陋,你可以在注册时为用户生成随机的头像(像素样式)。配置如下:
AVATARS_SAVE_PATH = os.path.join(ALBUMY_UPLOAD_PATH, 'avatars') AVATARS_SIZE_TUPLE = (30, 100, 200) # 同时生成三种尺寸的头像
然后就可以在User模型中添加生成头像的代码。扩展会自动生成和保存图像到文件系统中,你只需要再将文件的信息保存到数据库中即可。
from flask_avatars import Identicon avatar = Identicon() filenames = avatar.generate(text=self.username) # 可以通过一些参数进行更加丰富的配置(p336)
你可以创建一个类似Flask内置的static视图的视图函数,以方便在模板中通过url_for()
函数访问图片文件。(p337)
9.7 图片展示与管理
在用户的主页,可以展示图片的列表。如果你的用户对象使用location字段存储城市信息,你还可以将这个字符渲染为链接,通过将城市值拼接到Google地图的查询链接后,点击可以跳转到Gootle地图显示相关结果。
<!-- target="_blank" : 在新窗口打开链接 --> <a href="https://www.google.com/maps?q={{ user.location }}" target="_blank"> {{ user.location | truncate(20) }} </a>
truncate过滤器可以将字符截取到指定长度后显示。
在图片标签的外部添加一个标签,可以用于访问图片的详情页面(即实现通过点击图片进行页面跳转)。(p340)
{# 删除按钮 #} <a class="btn" data-tggle="modal" data-target="#confirm-delete" data-href="...">Delete</a> {# 模态框 #} <div class="modal fade" id="confirm-delete" ...></div>
然后通过JavaScript代码,监听模态框的打开事件。(p345)
$('#confirm-delete').on('show.bs.modal', function(e){ $('.delete-form').attr('action', $(e.relatedTarget).data('href')); })
Flask狼书笔记 | 09_图片社交网站 - 大型项目的架构与需求(2):https://developer.aliyun.com/article/1407227?spm=a2c6h.13148508.setting.25.79f64f0ecKMDuK