自学python 进击之路 - Django-高级

457 阅读9分钟

src=http___5b0988e595225.cdn.sohucs.com_images_20190916_32ce26032b124c3cb145a4231627373d.jpeg&refer=http___5b0988e595225.cdn.sohucs.jpg

缓存

  • 定义:缓存是一类可以更快的读取数据的介质统称,也指其他可以加快数据读取的存储方式,一般用来存储临时数据,常用介质的是读取速度很快的内存
  • 意义:视图渲染有一定成本,数据库的频繁查询过高,所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数;用户拿到响应的时间成本会更低

缓存案例:

# 案例分析
from django.shortcuts import render

def index(request):
   # 时间复杂度极高的渲染
   book_list = Book.objects.all()  
   return render(request , 'index.html', locals())

Django中设置缓存-数据库缓存

# 将缓存的数据存储在你的数据库中
# 说明:
    # 尽管存储介质没有更换。但是当把一次负责查询的结果直接储存到表里, 比如对个条件的过滤查询结果,可避免重复进行复杂查询,提升效率;
   CACHES= {
      'default':{
         'BACKEND':'django.core.cache.backends.db.DatabaseCache',
         'LOCATION':'my_cache_tadle',
         'TIMEOUT':300, # 缓存保存时间 单位秒 。默认值为300
         'OPTIONS': {
             'MAX_ENTRIES':300, # 缓存最大数据条数
             'CULL_FREQUENCY':2  # 缓存条数达到最大时 。删除1/x 的缓存数据
         }
         
      }
   }

Django中设置缓存- 本地内存缓存

# 数据缓存到服务内存中
# 配置样例:
CACHES = {
     'default': {
        'BACKEEND':
        'django.core.cache,backends.locmem.LocMemCache',
         'LOCATION': 'unique-snowflake'
     }
}

案例:

  1. 创建mysite7的项目
# 创建项目
django-admin startproject mysite7

2.创建数据库

# 创建名为mysite7的数据库
mysql> create database mysite7 default charset utf8;
# 连接数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mysite7',
        'USER': 'root',
        'PASSWORD': 'aa123456',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

3.添加缓存配置


# 数据库缓存配置 需要手动执行 创建表 的命令
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
        'TIMEOUT': '300',  # 缓存保存时间 单位秒, 默认值为300
        'OPTIONS': {
            'MAX_ENTRIES': 300,  # 缓存最大数据条数
            'CULL_FREQUENCY': 2,  # 缓存条数达到最大值 删除1/x 的缓存数据
        },
    }
}
# 在终端执行
python3 manage.py createcachetable

查看数据库

Snipaste_2021-07-28_14-48-55.png

整体缓存策略

Django 中使用缓存 - 视图函数中

# 例子
from django.views.decorators.cache import cache_page


@cache_page(30)  --> 单位s
def my_view(request):
  . . .. .

Django 中使用缓存 - 路由中

# 例子
from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo/', cache_page(60)(my_view))
]

缓存api的使用

先引入cache对象

# 方式1:使用caches['CACHE配置key'] 导入具体对象
from django.core.cache imort caches
cache1 = caches['myalias']
cache2 = caches['myalias_2']

#方式2:
from django.core.cache import cache 相当于直接引入CACHES配置项中'default'
  1. cache.set(key, value, timeout) - 存储缓存

    key: 缓存的key,字符串类型

    value:Python对象

    timeout: 缓存存储时间(s), 默认为CACHES中的TIMEOUT值

    返回值:None

  2. cache.get(key) - 获取缓存

    key:缓存的key

    返回值:为key的具体值。如果没有数据,则返回None

  3. cache.add(key, value) - 存储缓存, 只在key不存在时生效

    返回值:True[存储缓存]or False[存储失败]

  4. cahe.get_or_set(key, value, timeout) - 如果未获取到数据 则执行set 操作返回值:value

  5. cache.set_many(dict, timeout) - 批量存储缓存

    dict:key和value的字典

    timeout:存储时间(s)

    返回值:插入不成功的key的数组

  6. cache.get_many(key_list) - 批量获取缓存数据

    key_list包含key的数组

    返回值:取到的key和value的字典

  7. cache.delete(key)- 删除key的缓存数据

    返回值:None

  8. cache.delete_many(key_list) - 批量删除返回值:None

浏览器缓存策略

强缓存

1.响应头-Espires

定义:缓存过期时间, 用来指定资源到期的时间。是服务器端的具体时间点

例子:Expires:Thu, 02 Apr 2030 05:14:08 GMT

2.响应头 - Cache-Control

在HTTP/1.1中, Cache-Control 主要用于控制网页缓存。比如当`Cache-Control:max-age = 120`代表请求创建时间后的120秒 缓存失效
说明:目前服务器都会带着这两个头同时响应给浏览器。浏览器优先使用Cache-Control

协商缓存

强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据;

当强缓存时间到期时,浏览器将与服务器协商当前缓存是否可用。 如果是,服务器不需要返回数据。 浏览器将继续使用原始缓存数据,如果文件不可用,则返回最新的数据

  1. Last-Modified响应头和if-Modified-Since请求头

    说明:

    1.1 Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需协商的缓存

    1.2 当缓存到期后,浏览器将获取到的Last-Modified值做为请求头if-Modifide-Since的值,与服务器发请求协商,服务端返回304响应码[响应体为空], 代表缓存继续使用,200影响代表缓存不可用[响应体为最新资源]

  2. ETag响应头 和 if -None-Match请求头

    说明:

    2.1 Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化, Etag就会重新生成;

    2.2 缓存到期后。 浏览器将Etag响应头的值做为if - None- Mactch请求头的值,给服务器发请求协商。服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码[响应体为最新资源]; 可用则返回304响应码

中间件

定义

- 中间件是Django请求/响应处理的钩子框架,它是一个轻量级的‘低级的‘插件’系统, 用于全改变Django的输入或输出
- 中间件以类的形式体现
- 每一个中间件组件负责做一些特定的功能,例如Django包含一个中间件组件AuthenticationMiddleware, 它使用会话将用户与请求关联起来。
- 中间件是Django请求/响应处理的钩子框架,它是一个轻量级的‘低级的‘插件’系统, 用于全改变Django的输入或输出
- 中间件以类的形式体现
- 每一个中间件组件负责做一些特定的功能,例如Django包含一个中间件组件AuthenticationMiddleware, 它使用会话将用户与请求关联起来。

编写中间件

    -中间件类须继承自django.utils.deprecation.MiddlewareMixin类
-中间件类须实现下列五个方法中的一个多个:
   - process_request(self, request)
     执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
   - process_view(self, request, callback, callback_args, callback_kwargs)
     调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
   - process_response(self, request, response)
      所有响应返回浏览器被调用, 在每个请求上调用返回HttpResponse对象
   - process_exception(self, request, exception)
      当处理过程中抛出异常时调用,返回一个HttpResponse对象
      
注:中间件中的大多数方法在返回None时表示忽略当前操作进去下一项事件,当返回HttpResponse对象时表示此请求结束直接返回给客户端

注册中间件

# setting.py 中需要注册一下自定义的中间件
MIDDEWARE = [
  .. . . 
]
# 注意:配置为数组, 中间件被调用时以'先上到下'再'由上到上'的顺序调用

Snipaste_2021-07-28_21-31-50.png

练习1

# 用中间件实现强制某个ip地址只能向/test开头的地址发送5次请求
提示:
   request.META['REMOTE_ADDR']可以得到远程客户端的IP地址
   request.path_info 可以得到客户端访问的请求路由信息

Snipaste_2021-07-28_21-52-00.png

CSRF攻击

# CSRF - 跨站伪造请求攻击
某些恶意网站上包含连接、 表单按钮或者JavaScript,他们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作, 这就是跨站请求伪造(CSRE, 即Cross-Site Request Forgey)

CSRF防范

  • Django采用'比对暗号' 机制 防范攻击

  • Cookies中存储暗号1, 模板中,表单里藏着 暗号2, 用户只有在本网站下提交数据,暗号2才会随表单提交给服务器,django对比暗号, 对比成功 则认为是合法请求,否则是违法请求-403响应码

  • 配置步骤:

    1. settings.py 中确认MIDDLEWARE中 django.middleware.csrf.CsrViewMiddleware是否打开

    2. 模板中, form标签下添加如下标签

      {% csrf_token %}

  • 特殊说明: -- 如果某个视图不需要django进行csrf保护, 可以用装饰器关闭对此视图的检查


# 例子
from django.views.dacorators.csrf import cserf_exempt

@csrf_exempt
def my_view(request):
    return HttpResponse('hello')

分页

分页定义

- 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中显示部分数据
- 优点:
    1. 方便阅读
    2.减少数据提取量,减轻服务器压力
    
-- Django提供了Paginator类可以方便的实现分页功能
-- Paginator 类位于`django.core.paginator` 模块中

Paginator

# paginator对象
- 负责分页数据整体的管理
- 对象的构造方法
paginator = Paginator(object_list, per_page)
     - 参数
       - object_list 需要分类个数据的对象列表
       - per_page 每页数据个数
     - 返回值:
      -paginator的对象

Paginator 属性

- count: 需要分页数据的对象总数
- num_pages: 分页后的页面总数
- page_range: 从1开始的range对象,用于记录当前面码数
- per_page:每页数据的个数

paginator方法

paginator对象.page(numder)
     - 参数number为页码信息(从1开始)
     - 返回当前numder页对应的页信息
     - 如果提供的页码不存在, 抛出lnvalidPage异常

Paginator 异常exception

lnvaidPage: 总的异常基类,包含以下两个异常子类
    - PageNotAnlnteger: 当向page()传去一个不是整数的值时抛出
    - EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出

page对象 定义

负责具体某一页的数据的管理
- 创建对象
    Paginator 对象的Page()方法返回page对象
    page = paginator.page(页码)
- Page对象属性
    - object_list: 当前页上所有数据对象的列表
    - numder: 当前页的序号,从1开始
    - paginator:当前page对象相关的Paginator对象
 - has_next():  如果有下一页返回true
 -  has_previous(): 如果有上一页返回true
 - has_other_pages() : 如果有上一页或下一页返回true
 - next_page_number(): 返回下一页的页码,如果下一页不存在,抛出lnvalidPage异常
 - previous_page_number() : 返回上一页的页码。如果上一页不存在,抛出lnvalidPage异常

分页小案例

  • 创建视图
def test_page(request):
    page_num = request.GET.get('page', 1)
    all_data = ['a', 'b', 'c', 'd', 'e']
    # 初始化Paginator
    paginator = Paginator(all_data, 2)
    #  初始化具体页码的page对象
    c_page = paginator.page(int(page_num))
    return render(request, 'test_page.html', locals())
  • 配置路由

path('test_page', views.test_page)
  • 创建模板
# 创建templates文件夹
# 创建test_page.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>发页</title>
</head>
<body>
{% for p  in c_page %}
   <P>
    {{ p }}
   </P>

{% endfor %}

{% if c_page.has_previous %}
   <a href="/test_page?page={{ c_page.previous_page_numder }}">上一页</a>
{% else %}
上一页
{% endif %}

{% for p_num in paginator.page_range  %}
    {% if p_num == c_page.number %}
        {{ p_num }}
    {% else %}
        <a href="/test_page?page={{ p_num }}">{{ p_num }}</a>
    {% endif %}

{% endfor %}


{% if c_page.has_next %}
   <a href="/test_page?page={{ c_page.next_page_number }}">下一页</a>
{% else %}
下一页
{% endif %}
</body>
</html>

生成csv文件

在Python中生成csv文件

Python提供了内键库-csv可以直接通过该库操作csv文件
案例:
 import csv
 with opne('eggs.csv', 'w', newline='')as csvfile:
      writer = csv.writer(csvfile)
      writer.writerow(['a', 'b', 'c'])

csv文件下载

- 在网站中,实现下载csv,注意如下:
   - 响应Content-Type类型需要修改为text/csv. 这告诉浏览器该文档是csv文件,而不是HTML文件
   - 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称,它将被浏览器用于开启‘另存为’对话框

案例


# 示例代码
import csv
from django.http import HttpResponse
from .models import Book

def make_csv_view(requset):
    response = HttpResponse(content_type = 'text/csv')
    response['Content-Disposition'] = 'attachment;filename = 'mybook.csv''
    all_book = Book.objects.all()
    writer = csv.writer(response)
    writer.writerow(['id', 'title'])
    for b in all_book:
        writer.writerow[(b.id, b.title)]
        
    return response