轻量级Django 读书笔记-第二章

无状态的Web应用

参考代码
  无状态指的是后台Web应用程序认为每个请求都是独立的,没有前后的逻辑关系,就像一个门卫,看人只看票,谁拿着票都无所谓。
  为什么是无状态的呢?这个和互联网的基础架构以及一些历史原因有关,有兴趣可以参考HTTP协议

无状态的好处

  • 易于扩展
    将不同的应用可以方便的组织起来
  • 易于创建缓存
    不用为不同的状态创建不同缓存,缓存系统易于创建和维护
  • 易于负载均衡
    和易于扩增类似,可以通过增加服务器来让Web应用的性能更高,不必考虑有关联的不同请求被分配到不同工作服务器上所带来的问题

无状态的应用
  现代的大多数Web应用是要求有状态的,但都是基于这种无状态的结构而来的,服务器端是可以维护状态的,但需要客户端在每次请求时带上特殊的标记,以便服务器客户还原出这个请求的特征,从而判断是否一个相关请求
  对于简单内容展示,产品介绍以及公共信息展示等应用就不必考虑状态问题,做到性能足够好就可以了,这一章的实例中就展示了一个无状态的显示图片的Web应用示例——图片站位服务器

图片占位服务器

  作用很大,对于一个展示网页设计样式的Demo来说,图片占位服务器可以按照请求的要求,生成对应的图片,可以大大提高Demo的制作成本

构件

  • 视图
    • 首页视图
      展示主要的页面 index
    • 图片请求处理视图
      按要求处理生成图片,并将图片内容返回 placeholder
  • URL
    为每个视图创建一个URL规则
    对于图片请求,需要在请求中加入图片的规则,这里只有高和宽,可以自己加上颜色样式等其他信息
    URL中的参数
    Django2.0用path方法代替url,在url参数设置方面更为简单

    1
    path('image/<int:width>x<int:height>/', placeholder, name='placeholder')

    表示url中以image开头,后面匹配一个 dddxddd的参数,ddd必须是正整数(对于负数,由于前面要加上负号,就不会被这个url规则匹配)
    将匹配好的参数传入到视图 placeholder中,会自动拆分成 width,height

    1
    2
    3
    def placeholder(request, width, height):
    ...
    pass

    Django内置的路径转换器类型 参考 Django 2.0 新款URL配置详解

标识 例子 解释
str api/<str:name> 匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的
int image/<int:width> 匹配0和正整数,返回一个int类型
slug api/<slug:slug> 可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如’ building-your-1st-django-site‘
uuid api/<uuid:uuid> 匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象
path file/<path:filepath> 匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串
  • 生成图片
    • 利用Pillow库,生成图片,默认填充为黑色,并且将图片规格画在上面
    • 设置图片格式,和颜色类型(RGB)
    • 返回图片的字节码,这里没有去存储图片,性能会更高,同时为后面的建立图片缓存做好准备

请求数据验证

只要有人可以参与的流程都需要做验证,人是不靠谱的。
验证对程序员来说是个枯燥的苦差事,Django有办法可以帮到你。

1. 建立Form验证类

Django提供了Form类最为和前端Html form的对应,可以定义字段,并且可以设置字段的类型和数据范围,详细的默认字段可以参考Django字段类型

2. 初始化验证实体

将来自客户端提交的数据做初始化参数,得到一个form实例

1
2
3
...
form = ImageForm({'heigth':heigth, 'width': width})
...

用这个form实例就可以检验初始化参数是否符合规则了

例子中,图片的规则是通过GET,用url送来的,所以先要将规则参数从url中解析出来(见上面的 转换器部分),如果通过POST送来的数据初始化from是直接将request.POST作为参数就好了,参考Django Form

1
form = ImageForm(request.POST)

3. 利用is_valid()检验数据是否合法

is_valide方法会逐个检查定义在Form中的字段,如果都没问题会返回 True,这时可以用Form实例的cleaned_data属性来获取字段,并且得到的字段类型是经过转换的。如果返回的不是False说明验证失败了,如果需要得到具体的错误,可以冲from.errors中的获取,类似这样的

1
2
3
4
5
>>> form.errors
{
'sender': ['Enter a valid email address.'],
'subject': ['This field is required.']
}

根据校验结果返回合适的response,也可以将验证错误信息返回给前台,这样前台就可以标记哪些字段出了什么错

缓存

缓存能很好降低服务器的负荷,提高相应效率

  • 建立缓存机制

    1. 利用django.core.cache,作为缓存
    2. 为每个图片资源创建一个唯一的Key,比如用高宽和文件类型,key = "{}.{}.{}".format(width, height, image_format)
    3. 生成图片之前先合成图片的key,在cache中查找,找到即可返回,否则才创建,并且加入到缓存中

      缓存是个复杂的话题,涉及到key,命中率,淘汰规则等

      Django默认使用本地过程、内存缓存,但也可以使用不同的后端(像 Memcached或者文件系统),需要通过CACHES设置来配置

  • ETag

    ETag或实体标签(entity tag)是万维网协议HTTP的一部分。 是HTTP协议提供的若干机制中的一种Web缓存验证机制,并且允许客户端进行缓存协商。这就使得缓存变得更加高效,而且节省带宽。如果资源的内容没有发生改变,Web服务器就不需要发送一个完整的响应。
    摘自wiki HTTP ETag

  1. 定义一个ETag生成方法,方法接收与视图一样的参数

    1
    2
    3
    def generate_etag(request, width, height):
    ...
    return hashlib.sha1(...)
  2. 为视图加上@etag注解

    1
    2
    3
    @etag(generate_etag)
    def placeholder(request, width, height):
    ...

注意:
书中的例子中,视图定义为function,而在一般情况先视图定义为class
对于function可以用注解来加etag,对于class,需要在URL路由规则中加,如:

1
2
3
4
5
# 在url.py中,如果在views.py模块中定义了 Placeholder 视图类
from . import views
...
path('', etag(generate_etag)(views.Placeholder.as_view()), name='placeholder')
...

感悟

  • Web应用是建立在无状态的Http协议的基础上的,这个是学习和开发Web应用的基础
  • Http协议中定义了很多好玩的东西,并且不会因为定义很多影响使用,而是给想进一步的开发者提供更多的便利和支持,例如ETaglast_modified