• pt28django教程


    缓存

    缓存是一类可以更快的读取数据的介质统称,读取硬盘、较复杂的计算、渲染都会产生较大的耗时。数据变化频率不会很高的场景适合使用缓存。使用缓存场景:博客列表页、电商商品详情页、缓存导航及页脚。

    Django中设置缓存

    Django中提供多种缓存方式,如需使用需要在settings.py中进行配置

    初始化配置
    [root@vm ~]# mysql -uroot -p123456 -e 'create database mysite7 default charset utf8;'
    
    [root@vm ~]# django-admin startproject mysite7
    [root@vm ~]# cd mysite7/
    [root@vm mysite7]# vim mysite7/settings.py
    
    ALLOWED_HOSTS = ['*',]
    
    DATABASES = {
        'default' : {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'mysite7',
            'USER': 'root',
            'PASSWORD': '123456',
            'HOST': '127.0.0.1',
            'PORT': '3306',
        }
    }
    LANGUAGE_CODE = 'zh-Hans'
    
    TIME_ZONE = "Asia/Shanghai"
    
    [root@vm mysite7]# python3 manage.py runserver 0.0.0.0:8000
    [root@vm mysite7]# python3 manage.py migrate
    [root@vm mysite7]# mysql -uroot -p123456 -e 'use mysite7;show tables;'
    
    • 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
    1、数据库缓存

    mysite7 改配置 migrate , 添加缓存配置项 createcachetable

    Django可以将其缓存的数据存储在您的数据库中

    [root@vm mysite7]# vim mysite7/settings.py
    #添加到最后
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
            'LOCATION': 'my_cache_table',
            'TIMEOUT': 300,  #缓存保存时间 单位秒,默认值为300, 
            'OPTIONS':{
                'MAX_ENTRIES': 300, #缓存最大数据条数
                'CULL_FREQUENCY': 2,#缓存条数达到最大值时 删除1/2的缓存数据
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建缓存表

    [root@vm mysite7]# python3 manage.py createcachetable
    [root@vm mysite7]# mysql -uroot -p123456 -e 'desc mysite7.my_cache_table;'
    +-----------+--------------+------+-----+---------+-------+
    | Field     | Type         | Null | Key | Default | Extra |
    +-----------+--------------+------+-----+---------+-------+
    | cache_key | varchar(255) | NO   | PRI | NULL    |       |
    | value     | longtext     | NO   |     | NULL    |       |
    | expires   | datetime(6)  | NO   | MUL | NULL    |       |
    +-----------+--------------+------+-----+---------+-------+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2、文件系统缓存
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache',#这个是文件夹的路径
            #'LOCATION': 'c:\test\cache',#windows下示例
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    3、本地内存缓存
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'unique-snowflake'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Django中使用缓存

    在urls中使用(较少)
    from django.views.decorators.cache import cache_page
    
    urlpatterns = [
        path('foo/', cache_page(60)(my_view)  ),
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    在模板中使用(较少)
    {% load cache %}        
    
    {% cache 500 sidebar username %}
        .. sidebar for logged in user ..
    {% endcache %}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    在Views中使用
    from django.views.decorators.cache import cache_page
    
    @cache_page(30)  -> 单位s
    def my_view(request):
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用举例

    [root@vm mysite7]# vim mysite7/urls.py
    from . import views
    urlpatterns = [
        path('test_cache/',views.test_cache),
    ...
    
    [root@vm mysite7]# vim mysite7/views.py
    from django.http import HttpResponse
    import time
    
    def test_cache(request):
        t = time.time()
        # 经历了复杂计算或复杂查询
        time.sleep(10)
        return HttpResponse("time is %s" % t)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    http://192.168.1.11:8000/test_cache/     #访问很慢,network栏,看到用时10s
    
    • 1
    缓存装饰器

    不灵活,不方便修改清理

    [root@vm mysite7]# vim mysite7/views.py
    from django.http import HttpResponse
    import time
    from django.views.decorators.cache import cache_page
    
    @cache_page(600)
    def test_cache(request):
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    http://192.168.1.11:8000/test_cache/     #第一次很慢,后面很快,但是时间没变化
    [root@vm net_note]# mysql -uroot -p123456 -e 'select * from mysite7.my_cache_table\G'
    
    • 1
    • 2
    缓存api

    作用:局部缓存部分结果,

    set、get、delete、add 、get_or_set、set_many、get_many、delete_many

    #指定配置引入,多个缓存import  caches
    from django.core.cache import caches
    cache1 = caches['myalias']
    cache2 = caches['myalias_2']
    
    #默认配置引入【指的配置中的default项】 等同于 caches['default']
    from django.core.cache import cache
    
    #常规命令 set
    #key: 字符串类型
    #value: Python对象
    #timeout:缓存存储时间  默认值为settings.py CACHES对应配置的TIMEOUT
    #返回值:None
    cache.set('my_key', 'myvalue', 30)
    
    #常规命令 get
    #返回值:为key的具体值,如果没有数据,则返回None
    cache.get('my_key')
    #可添加默认值,如果没取到返回默认值
    cache.get('my_key', 'default值')
    
    #常规命令 delete
    #返回值  None
    cache.delete('my_key')
    
    #常规命令 add 只有在key不存在的时候 才能设置成功
    #返回值 True or False
    cache.add('my_key', 'value') #如果my_key已经存在,则此次赋值失效
    
    #常规命令 get_or_set 如果未获取到数据 则执行set操作
    #返回值 key的值
    cache.get_or_set('my_key', 'value', 10)
    
    #常规命令 get_many(key_list) set_many(dict,timeout)
    #返回值  set_many:返回插入不成功的key数组 
    #       get_many:取到的key和value的字典
    >>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
    >>> cache.get_many(['a', 'b', 'c'])
    {'a': 1, 'b': 2, 'c': 3}
    
    #常规命令 delete_many
    #返回值  成功删除的数据条数
    cache.delete_many(['a', 'b', 'c'])
    
    • 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
    [root@vm mysite7]# python3 manage.py shell
    >>> from django.core.cache import cache
    >>> cahe.set('user','django',600)
    >> cache.get('user')
    'django'
    >>> cache.delete('user')
    >>> cache.get('user')
    
    >>> cache.add('user','django',600)
    True
    >>> cache.add('user','django',600)
    False
    >>> cache.get('user')
    'django'
    
    >>> cache.get_or_set('user2','django',600)
    'django'
    >>> cache.get_or_set('user2','dj',600)
    'django'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    浏览器中的缓存

    强缓存

    不会向服务器发送请求,直接从缓存中读取资源

    Expires

    缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
    Expires: Thu, 02 Apr 2030 05:14:08 GMT
    Expires是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效
    
    • 1
    • 2
    • 3

    Cache-Control

    在HTTP/1.1中,Cache-Control主要用于控制网页缓存。
    比如当Cache-Control:max-age=120  代表请求创建时间后的120秒,缓存失效
    
    • 1
    • 2

    一般情况下两个值都会设置,浏览器查看Response Header里的Expires、Cache-Control

    协商缓存

    协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程

    Last-Modified和If-Modified-Since
    第一次访问时,服务器会返回文件的最近修改时间 Last-Modified: 
    浏览器下次请求时 携带If-Modified-Since这个header , 该值为 Last-Modified
    服务器接收请求后,对比结果,若资源未发生改变,则返回304,否则返回200并将新资源返回给浏览器
    
    • 1
    • 2
    • 3

    缺点:只能精确到秒,容易发生单秒内多次修改,检测不到

    ETag和If-None-Match

    ​ Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。流程同上,判断是否match。

    对比Last-Modified:Etag精度高、Etag优先级高、性能不如Last-Modifi

    中间件 Middleware

    中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。

    每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。

    中间件类:

    中间件类须继承自 django.utils.deprecation.MiddlewareMixin

    中间件类须实现下列五个方法中的一个或多个:

    • def process_request(self, request): 执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
    • def process_view(self, request, callback, callback_args, callback_kwargs): 调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
    • def process_response(self, request, response): 所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象
    • def process_exception(self, request, exception): 当处理过程中抛出异常时调用,返回一个HttpResponse对象
    • def process_template_response(self, request, response): 在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象

    注: 中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponese对象时表示此请求结束,直接返回给客户端

    编写中间件类:
    [root@vm mysite7]# mkdir middleware
    [root@vm mysite7]# vim middleware/mymiddleware.py
    
    from django.http import HttpResponse
    from django.utils.deprecation import MiddlewareMixin
    
    class MyMiddleWare(MiddlewareMixin):
        def process_request(self, request):
            print("中间件方法 process_request 被调用")
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("中间件方法 process_view 被调用")
    
        def process_response(self, request, response):
            print("中间件方法 process_response 被调用")
            return response
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    注册中间件:
    [root@vm mysite7]# vim mysite7/settings.py
    ...
    MIDDLEWARE = [
        ...
        'middleware.mymiddleware.MyMiddleWare',
           ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    使用测试验证
    [root@vm mysite7]# vim mysite7/urls.py
    urlpatterns = [
    ...
        path('test_mw', views.test_mw),
        path('test_mw2', views.test_mw2),
        
    [root@vm mysite7]# vim mysite7/views.py
    ...    
    def test_mw(request):
        print('---view in test_mw---')
        return HttpResponse("test mw is OK!")
    
    
    def test_mw2(request):
        print('---view in test_mw2---')
        return HttpResponse('test mw2 is OK!')    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    访问http://192.168.1.11:8000/test_mw    test mw is OK!
    终端日志:
        中间件方法 process_request 被调用
        中间件方法 process_view 被调用
        ---view in test_mw---
        中间件方法 process_response 被调用
    访问http://192.168.1.11:8000/test_mw2  也会有日志显示,影响全局 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    中间件执行的顺序

    重新复制一套MyMiddleWare2 访问,执行顺序如下,进和出方向正好相反(洋葱图了解)

    访问http://192.168.1.11:8000/test_mw 
        中间件方法 process_request 被调用
        中间件方法2 process_request 被调用
        中间件方法 process_view 被调用
        中间件方法2 process_view 被调用
        ---view in test_mw---
        中间件方法2 process_response 被调用
        中间件方法 process_response 被调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    练习

    用中间件实现强制某个IP地址只能向/test 发送 5 次GET请求
        提示:
          - request.META['REMOTE_ADDR'] 可以得到远程客户端的IP地址
          - request.path_info 可以得到客户端访问的GET请求路由信息
          - 在进url之前统计次数,做出处理
    
    • 1
    • 2
    • 3
    • 4
    • 5
    [root@vm mysite7]# vim middleware/mymiddleware.py
    from django.http import HttpResponse
    from django.utils.deprecation import MiddlewareMixin
    import re
    
    class MyMW(MiddlewareMixin):
        # 统计每一个客户端IP以及对应的访问次数
        visit_times = {}
    
        def process_request(self, request):
            # 1. 只对/test开头的路由做5次访问的限制
            if not re.match(r'^/test', request.path_info):
                return
            # 2. 获取客户端的IP地址
            cip = request.META['REMOTE_ADDR']
            # 3. 获取访问次数,回去cip的次数,默认是0次
            times = self.visit_times.get(cip, 0)
            # 4. 判断是否超过5次
            if times >= 5:
                return HttpResponse("no way!")
            # 5.每访问一次,次数+1
            self.visit_times[cip] = times + 1
            print('%s visit we %s times' % (cip,
                                            self.visit_times[cip]))
                                            
    [root@vm mysite7]# vim mysite7/settings.py
        'middleware.mymiddleware.MyMW',                                   
    
    • 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

    访问测试,看日志输出 192.168.1.1 visit we 1 times 5次后看浏览器返回no way

    跨站请求伪造攻击 CSRF

    跨站请求伪造攻击

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

    说明: CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。

    作用: 不让其它表单提交到此 Django 服务器

    防范步骤:

    打开MIDDLEWARE

    打开MIDDLEWARE中django.middleware.csrf.CsrfViewMiddleware,默认打开

    添加csrf_token

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

        <form action=....>
            {% csrf_token %}
            ...
        </form>
    
    • 1
    • 2
    • 3
    • 4

    关闭个别试图csrf

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

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def my_view(request):
        return HttpResponse('Hello world')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用测试

    [root@vm mysite7]# vim mysite7/urls.py
        path('test_csrf', views.test_csrf),
    
    [root@vm mysite7]# vim mysite7/views.py
    from django.http import HttpResponse
    from django.shortcuts import render
    
    def test_csrf(request):
        if request.method == 'GET':
            return render(request, "test_csrf.html")
        elif request.method == 'POST':
            name = request.POST['name']
            return HttpResponse("欢迎您,%s" % name)
     
    [root@vm mysite7]# vim mysite7/settings.py
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,'templates')],
             
    [root@vm mysite7]# mkdir -p templates
    [root@vm mysite7]# vim templates/test_csrf.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>CSRF</title>
    </head>
    <body>
        <form action="/test_csrf" method="post">
            <p>
                名称:<input type="text" name="name">
            </p>
            <input type="submit" value="提交">
        </form>
    </body>
    </html>
    
    • 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
    #访问  http://192.168.1.11:8000/test_csrf   输入名称,提交,
    # 报CSRF验证失败. 请求被中断. 看下面的help里的 {% csrf_token %} template tag
    #写到form表单里
        <form action="/test_csrf" method="post">
            {% csrf_token %}
            <p>
                名称:<input type="text" name="name">
            </p>
            <input type="submit" value="提交">
        </form>
        
    #再次访问查看网页源代码,多出一个csrf的token
    <input type="hidden" name="csrfmiddlewaretoken" value="oqLeUQSpIy...>
         点击"提交"按钮,中间件的方法先于视图函数被调用,在中间方法中
         验证,先前给你的csrfmiddlewaretoken,验证存在并正确
         放行,调用视图函数完成相应的功能。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    web分页功能

    分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据。

    好处: 方便阅读;减少数据提取量,减轻服务器压力。

    Django通过使用Paginator和Page这两个类来完成的分页

    Paginator对象

    负责分页数据整体的管理,位于django.core.paginator 模块中。

    对象的构造方法paginator = Paginator(object_list, per_page)
    参数
        object_list 需要分类数据的对象列表
         per_page 每页数据个数
    返回值: Paginator的对象
    
    Paginator属性
      - count:需要分类数据的对象总数
      - num_pages:分页后的页面总数
      - page_range:从1开始的range对象, 用于记录当前面码数
      - per_page 每页数据的个数
    
    Paginator方法
      - page(number)
        - 参数 number为页码信息(从1开始)
        - 返回当前number页对应的页信息
        - 如果提供的页码不存在,抛出InvalidPage异常
    
    Paginator异常exception
      - InvalidPage:总的异常基类,包含以下两个异常子类
        - PageNotAnInteger:当向page()传入一个不是整数的值时抛出
        - EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Page对象

    负责具体某一页的数据的管理,Paginator对象的page()方法返回Page对象

    创建对象   page = paginator.page(页码)
    
    Page对象属性
        - object_list:当前页上所有数据对象的列表
        - number:当前页的序号,从1开始
        - paginator:当前page对象相关的Paginator对象
    
    Page对象方法
        - has_next():如果有下一页返回True
        - has_previous():如果有上一页返回True
        - has_other_pages():如果有上一页或下一页返回True
        - next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
        - previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
        - len():返回当前页面对象的个数
        
    说明:  Page 对象是可迭代对象,可以用 for 语句来 访问当前页面中的每个对象
    参考文档 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    分页示例:

    视图函数

    from django.core.paginator import Paginator
    def book(request):  
        # 1. 准备要分页的数据
        bks = Book.objects.all()
        # 2. 创建分页器    
        paginator = Paginator(bks, 10)
        # 3. 获取当前页码
        cur_page = request.GET.get('page', 1)
        # 4. 根据页码获取页面对象
        page = paginator.page(cur_page)
        # 5. 返回响应
        return render(request, 'bookstore/book.html', locals())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    模板设计

    <html>
    <head>
        <title>分页显示title>
    head>
    <body>
    {% for b in page %}
        <div>{{ b.title }}div>
    {% endfor %}
    
    {% if page.has_previous %}
    <a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页a>
    {% else %}
    上一页
    {% endif %}
    
    {% for p in paginator.page_range %}
        {% if p == page.number %}
            {{ p }}
        {% else %}
            <a href="{% url 'book' %}?page={{ p }}">{{ p }}a>
        {% endif %}
    {% endfor %}
    
    {% if page.has_next %}
    <a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页a>
    {% else %}
    下一页
    {% endif %}
    body>
    html>
    
    • 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

    使用示例

    [root@vm mysite7]# vim mysite7/urls.py
        path('test_page', views.test_page,
             name='book'),
    
    [root@vm mysite7]# vim mysite7/views.py
    ...
    from django.core.paginator import Paginator
    def test_page(request):
        # 1. 准备要分页的数据
        list_data = ['a', 'b', 'c', 'd', 'e']
        # 2. 创建分页器
        paginator = Paginator(list_data, 2)
        # 3. 获取当前页码
        cur_page = request.GET.get('page', 1)
        # 4. 根据页码获取页面对象
        page = paginator.page(cur_page)
        # 5. 返回响应
        return render(request, "test_page.html",
                      locals())
                      
    [root@vm mysite7]# vim templates/test_page.html
    <html>
    <head>
        <title>分页显示</title>
    </head>
    <body>
    {% for b in page %}
        <div>{{ b }}</div>
    {% endfor %}
    
    {% if page.has_previous %}
    <a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页</a>
    {% else %}
    上一页
    {% endif %}
    
    {% for p in paginator.page_range %}
        {% if p == page.number %}
            {{ p }}
        {% else %}
            <a href="{% url 'book' %}?page={{ p }}">{{ p }}</a>
        {% endif %}
    {% endfor %}
    
    {% if page.has_next %}
    <a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页</a>
    {% else %}
    下一页
    {% endif %}
    </body>
    </html>     
    
    #访问验证http://192.168.1.11:8000/test_page
    
    • 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
    • 53

    csv文件下载

    Django可直接在视图函数中生成csv文件 并响应给浏览器

    import csv
    from django.http import HttpResponse
    from .models import Book
    
    def make_csv_view(request):
        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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 响应获得一个特殊的MIME类型text / csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
    • 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。它将被浏览器用于“另存为…”对话框
    • 对于CSV文件中的每一行,调用writer.writerow,传递一个可迭代对象,如列表或元组。
    [root@vm mysite7]# vim mysite7/urls.py
        path('test_csv', views.test_csv),
    [root@vm mysite7]# vim mysite7/views.py
    import csv
    from django.http import HttpResponse
    from .models import Book
    
    def test_csv(request):
        # 1. 创建响应对象时,指定响应的内容的类型是csv
        response = HttpResponse(content_type='text/csv')
        # 2. 告诉浏览器以附件的方式处理响应数据,文件名为mybook.csv
        response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
        # 3. 获取数据
        # all_book = Book.objects.all()
        all_book = [
            {'id': 1, 'title': 'java'},
            {'id': 2, 'title': 'python'},
            {'id': 3, 'title': 'php'},
        ]
        # 4. 创建一个csv格式的写入器,将数据写入到response
        writer = csv.writer(response)
        # 5. 调用写入器的方式writerow,写入一行数据,标题
        writer.writerow(['编号', '图书名称'])
        # 6. 写入数据
        for b in all_book:
            writer.writerow([b['id'], b['title']])
        return response
    
    
    #访问  http://192.168.1.11:8000/test_csv     产生下载
    
    • 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
  • 相关阅读:
    Redis配置文件
    51单片机基础(C语言):定时器时钟
    动态路由协议OSPF项目部署(二)
    localStorage 和 sessionStorage 异同
    关系数据库与文档数据库对比
    Mysql-联合查询及子查询
    gradle android 配置 build 变体
    软考高级系统架构设计师系列之:案例分析典型试题五
    Kotlin中正确的使用Handler
    (2023,域泛化 & 信息论 & 特征解缠)INSURE:信息论启发的域泛化解缠结和纯化模型
  • 原文地址:https://blog.csdn.net/weixin_60092693/article/details/131620921