Django的边边角角(二):缓存、中间件、分页器、富文本

一.缓存

1.概述

目的
  在Django中,当用户请求到达视图后,视图会先从数据库提取数据放到模板中进行动态渲染,渲染后的结果就是用户看到的网页。如果用户每次请求都从数据库提取数据并渲染,将极大降低性能,不仅服务器压力大,而且客户端也无法即时获得响应。如果能将渲染后的结果放到速度更快的缓存中,每次有请求过来,先检查缓存中是否有对应的资源,如果有,直接从缓存中取出来返回响应,节省取数据和渲染的时间,不仅能大大提高系统性能,还能提高用户体验。
应用场景
  缓存主要适用于对页面实时性要求不高的页面。存放在缓存的数据,通常是频繁访问的,而不会经常修改的数据。
我们来举几个应用例子:

博客文章。假设用户一天更新一篇文章,那么可以为博客设置1天的缓存,一天后会刷新。

购物网站。商品的描述信息几乎不会变化,而商品的购买数量需要根据用户情况实时更新。我们可以只选择缓存商品描述信息。

缓存网页片段。比如缓存网页导航菜单和脚部(Footer)。

2.Django内置缓存实现

(1)装饰器缓存

这是系统封装的,需要注意的是装饰器参数不需要写timeout
创建子路由:

url(r'^testCache/',views.testCache),

生成视图函数:

def testCache(request):
    time.sleep(5)
    return HttpResponse('敌军还有5秒抵达战场')

运行结果:
在这里插入图片描述
我们会发现每次刷新都需要等待5秒页面才显示。
使用装饰器缓存:

@cache_page(30)
def testCache(request):
    time.sleep(5)
    return HttpResponse('敌军还有5秒抵达战场')

我们会发现除了第一次运行是等待5秒后,在30秒内不需要再等待了

(2)数据库缓存

基于数据库,在真实的数据库中去创建缓存表
创建缓存表:

python manage.py createcachetable my_cache_table

创建成功后,我们会发现数据库中有my_cache_table表了
在这里插入图片描述
在settings中配置缓存信息:

CACHES={
              'default':{
                  'BACKEND':'django.core.cache.backends.db.DatabaseCache',
                  #缓存的位置
                  'LOCATION':'my_cache_table',
                  #过期时间
                  'TIMEOUT':60,
                  #前缀
                  'KEY_PREFIX':'python2001',
              }
           }

创建子路由:

url(r'^testCache1/',views.testCache1),

生成视图函数:

def testCache1(request):
    #导入  django.core.cache.cache
    value = cache.get('ip')

    if value:
        return HttpResponse(value)
    else:
        # ip地址
        ip = request.META.get('REMOTE_ADDR')
        cache.set('ip',ip)

        return HttpResponse('来了老弟')

在这里插入图片描述
60秒后,再次刷新:
在这里插入图片描述

(3)内存级数据库缓存(redis)

基于redis-内存级数据库

Django-redis-cache
	使用redis实现django-cache的扩展
	操作缓存的API没有发生任何变更
	变更的就是连接缓存的配置

安装 django-redis 和 django-redis-cache:
在这里插入图片描述
在这里插入图片描述

在settings中配置缓存信息:

CACHES={
           'default':{
                'BACKEND':'django_redis.cache.RedisCache',
                # 缓存的位置
                # redis一共有16个数据库  0 ~ 15
                # 6379:redis端口号
                'LOCATION':'redis://127.0.0.1:6379/1',
                'OPTIONS':{
                    'CLIENT_CLASS':'django_redis.client.DefaultClient'
                        },
                'KEY_PREFIX': 'django2001',
                    },
        }

创建子路由:

url(r'^testCache2/',views.testCache2),

生成视图函数:

def testCache2(request):

    value = cache.get('ip')
    if value:
        return HttpResponse('你在缓存中拿到的数据')
    else:
        ip = request.META.get('REMOTE_ADDR')
        cache.set('ip',ip)

        return HttpResponse('你是从数据库拿的数据')

运行结果:
在这里插入图片描述

如果你的redis中报错,端口号已经被占用,那么就是因为你的这个服务已经开启
可以使用ps -ef | grep -i redis 来查看,如果已经开启,不要急于杀死进程
解决策略:将settings中的配置中redis的主机地址设置为127.0.0.1
如果你连接redis的时候出现转圈的情况
(1)查看云主机的入方向和出方向的端口 必须要开放6379端口
基本端口 22:ssh , 80:http , 443:https , 3306:mysql , 6379:redis , 8000:django的默认端接口
入方向和出方向,必须一致
以下策略基本上在linux操作系统出现的概率不大,如果有按照以下方案来改
(2)配置文件中不允许远程连接
56行代码是 bind 127.0.0.1 将这行代码注释,因为默认是只允许本地连接
76行代码是protect_model = yes 修改为no

3.多级缓存

写多套配置,定义不同的名字,存入缓存的时候,获取不同的缓存对象,想使用哪个缓存就创建哪个缓存的实例对象
在settings中配置缓存信息:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
        'TIMEOUT': 60 * 5,
        'KEY_PREFIX':'databaseCache2001',
    },

    'django2001': {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
        'KEY_PREFIX':'redis2001',
        'TIMEOUT': 60 * 5,
    }
}

创建主路由:

url(r'^final/',include('FinalApp.urls',namespace='final')),

(1)多级缓存 - 装饰器缓存

创建子路由:

url(r'^testCache/',views.testCache),

生成视图函数:

#可以使用装饰器,指定想要的数据库
@cache_page(30,cache='default')
def testCache(request):
    time.sleep(5)
    return HttpResponse('欢迎来到英雄联盟')

运行结果:
在这里插入图片描述
查看缓存表中也有记录:
在这里插入图片描述

(2)多级缓存 - 缓存的基本应用

创建子路由:

url(r'^testCache1/',views.testCache1),

生成视图函数:

def testCache1(request):
    cache = caches['django2001']
    value = cache.get('ip')
    if value:
        return HttpResponse('有值')
    else:
        ip = request.META.get('REMOTE_ADDR')
        cache.set('ip',ip)
        return HttpResponse('没值')

第一次运行结果:
在这里插入图片描述
第二次运行结果:
在这里插入图片描述
查看是否存入redis:
在这里插入图片描述

(3)多级缓存 - 缓存时间就近原则

思考:在settings设置了缓存时间’TIMEOUT’: 60 * 5,如果又在装饰器缓存中设置了缓存时间,那么是执行哪个???
创建子路由:

url(r'^testCache2/',views.testCache2),

生成视图函数:

@cache_page(30,cache='django2001')
def testCache2(request):
    time.sleep(5)
    return HttpResponse('测试缓存的优先级')

运行结果:
在这里插入图片描述
当我们等30s后还没到5分钟的时候,再次刷新页面,会发现我们需要等待5s,所以缓存的时间以视图函数为主,就近原则

二.中间件

1.介绍

中间件:是一个轻量级的,底层的插件,可以介入到Django的请求和响应过程(面向切面编程)
中间件的本质就是一个python类
面向切面编程(Aspect Oriented Programming)简称AOP。AOP的主要实现目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。
在这里插入图片描述
django内置的一个底层插件
从属于面向切面编程AOP
  在不修改源代码的情况下,动态去添加一些业务逻辑处理
中间件的典型实现:装饰器
  中间件就是使用类装饰实现的

面向切面编程
切点
	(1)process_request
		process_request(self,request):在执行视图前被调用,每个请求上都会调用,不主动进行返回或返回HttpResponse对象
		
	(2)process_view
		process_view(self,request,view_func,view_args,view_kwargs):调用视图之前执行,每个请求都会调用,不主动进行返回或返回HttpResponse对象
		
	(3)process_template_response	
		process_template_response(self,request,response):在视图刚好执行完后进行调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象
		
	(4)process_response
		process_response(self,request,response):所有响应返回浏览器之前调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象
		
	(5)process_exception
		process_exception(self,request,exception):当视图抛出异常时调用,不主动进行返回或返回HttpResponse对象
切面
	切点处切开可以获得的数据

2.基本使用

(1)在工程目录下创建middleware目录
(2)目录中创建一个python文件
在这里插入图片描述
(3)在python文件中导入中间件的基类
       from django.utils.deprecation import MiddlewareMixin
(4)在类中根据功能需求,创建切入需求类,重写切入点方法:

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        print('i am first middleware')

(5)启用中间件,在settings中进行配置,MIDDLEWARE中添加: middleware.文件名.类名
在这里插入图片描述
创建子路由:

url(r'^testMiddleware/',views.testMiddleware),

生成视图函数:

def testMiddleware(request):
    return HttpResponse('天道酬勤')

运行结果:
在这里插入图片描述
思考:若中间件中有返回值,视图函数中有返回值,那么返回哪个???

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        print('i am first middleware')
        return HttpResponse('欢迎来到英雄联盟')

运行结果:
在这里插入图片描述

中间件是在执行视图函数之前执行,一般情况下,中间件没有返回值
如果中间件中有返回值了,那么就不在执行视图函数了

3.过滤资源路径

以增删改查为例
创建子路由:

url(r'add/',views.add),
url(r'delete/', views.delete),
url(r'update/', views.update),
url(r'find/', views.find),

生成视图函数:

def add(request):
    print('连接数据库')
    return HttpResponse('add')

def delete(request):
    print('连接数据库')
    return HttpResponse('delete')

def update(request):
    print('连接数据库')
    return HttpResponse('update')

def find(request):
    print('连接数据库')
    return HttpResponse('find')

由上可以发现,每次都需连接数据库,耦合度太高,这时就可以使用中间件来减少耦合度

def add(request):
    return HttpResponse('add')

def delete(request):
    return HttpResponse('delete')

def update(request):
    return HttpResponse('update')

def find(request):
    return HttpResponse('find')

在middleware.py文件中:

class TestMiddle(MiddlewareMixin):
    # 中间件是在执行视图函数之前执行,一般情况下,中间件没有返回值
    # 如果中间件中有返回值了,那么就不在执行视图函数了
    def process_request(self,request):
            print('中间件连接数据库')

运行结果:
在这里插入图片描述
在这里插入图片描述
我们可以发现增删改查都可以通过中间件来连接数据库,但是如果我们调用TestMiddle函数,会出现什么情况???
在这里插入图片描述
我们会发现在我们不需要连接数据库的时候,中间件也连接了数据库, 这是因为在默认情况下,中间件是作用在所有的视图函数的前面,所以我们需要过滤资源路径

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        CONN_REQ = [
            '/final/add/',
            '/final/delete/',
            '/final/update/',
            '/final/find/',
        ]
        if request.path in CONN_REQ:
            print('中间件连接数据库')

运行结果:
在这里插入图片描述
在这里插入图片描述

4.应用-白名单

创建子路由:

url(r'^testWhite/',views.testWhite),

生成视图函数:

def testWhite(request):
    if random.randrange(100) > 70:
        return HttpResponse('恭喜你获得三等奖,获得英雄联盟全英雄')
    else:
        return HttpResponse('很遗憾,英雄体验卡一张')

运行结果:
在这里插入图片描述

设置白名单:

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        # 白名单IP
        IP_REQ = ['xxx.xxx.xxx.xxx','xxx.xxx.xxx.xxx']
        ip = request.META.get('REMOTE_ADDR')
        print(ip)
        if ip in IP_REQ:
            if random.randrange(100) > 50:
                return HttpResponse('恭喜你中了一等奖,英雄联盟全英雄全皮肤全手办')

运行结果:
在这里插入图片描述

5.中间件异常

当某一段业务逻辑发生了错误,那么就会执行process_exception方法,process_exception使得界面友好化,应用交互友好化。
创建子路由:

url(r'^testException/',views.testException),

生成视图函数:

def testException(request):
    a = 1
    b = 0
    print(a / b)
    return HttpResponse('测试异常')

运行结果:
在这里插入图片描述
这个报错界面不是很友好,我们可以进行修改:
在中间件添加:

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        pass
    # 判断所有的视图函数,如果视图函数中有异常,然后就要执行process_exception
    def process_exception(self, request, exception):
        return HttpResponse('服务器中进入了一个只耗子,稍等片刻。。。')

运行结果:
在这里插入图片描述
我们也可以将它跳转到其他页面:

class TestMiddle(MiddlewareMixin):
    def process_request(self,request):
        pass
    def process_exception(self, request, exception):
        return redirect(reverse('final:index'))

运行结果:
在这里插入图片描述

6.中间件的执行顺序

创建新的中间件:

class SecondMiddleware(MiddlewareMixin):
    def process_request(self,request):
        print('i am second middleware')

在settings中再次注册一个中间件:

'middleware.middleware.SecondMiddleware',

创建子路由:

url(r'^testMiddleSort/',views.testMiddleSort),

生成视图函数:

def testMiddleSort(request):
    return HttpResponse('欢迎来到英雄联盟')

运行结果:
在这里插入图片描述
思考:执行顺序是书写顺序还是注册顺序???
当我们修改一下注册顺序试试:

'middleware.middleware.SecondMiddleware',
'middleware.middleware.TestMiddle',

运行结果:
在这里插入图片描述
由上可知,中间件的执行顺序是按照注册顺序执行的
思考:如果一个中间件进行了返回,其他的中间件是否会执行???
在settings中,中间件的注册顺序:

'middleware.middleware.TestMiddle',
'middleware.middleware.SecondMiddleware',

中间件:

class TestMiddle(MiddlewareMixin):
    # 中间件是在执行视图函数之前执行,一般情况下,中间件没有返回值
    # 如果中间件中有返回值了,那么就不在执行视图函数了
    def process_request(self,request):
        print('i am first middleware')
        return HttpResponse('敌军还有5秒抵达战场')

class SecondMiddleware(MiddlewareMixin):
    def process_request(self,request):
        print('i am second middleware')

运行结果:
在这里插入图片描述
由上可知,如果一个中间件进行了返回,其他的中间件不会执行

总结:
中间件的执行顺序
   中间件注册的时候是一个列表
   如果我们没有在切点处直接进行返回,中间件会依次执行
   如果我们直接进行了返回,后续中间件就不再执行了

三.分页器

分页是为了提升用户体验,并且减小服务器的负担而开发的
分页:
  真分页:每一次点击下一页或者上一页,都会向数据库发送请求,并且返回数据,访问数据库次数过多
  假分页:一次性读取所有数据,然后在内存中进行分页
企业级开发中常用的是真分页

1.原生实现

创建模型:

class Student(models.Model):
    name = models.CharField(max_length=32)

    class Meta:
        db_table = 'student'

添加数据:
在这里插入图片描述
创建子路由:

url(r'^testGetPage/',views.testGetPage),

生成视图函数:

def testGetPage(request):
    #页数
    page = int(request.GET.get('page',1))
    #设置每页5条数据
    per_page = int(request.GET.get('per_page',5))
    # 规律
    #  1   0 5
    #  2   5 10
    #  3   10 15
    #  4   15 20
    #查询
    student_list = Student.objects.all()[(page-1)*per_page:per_page*page]

    for student in student_list:
        print(student.id,student.name)

    return HttpResponse('分页原生实现')

运行结果:
在这里插入图片描述

2.封装实现

使用Django自带的Paginator

Paginator(分页工具)
	对象创建
		:Paginator(数据集,每一页数据数)
		paginator = Paginator(students, per_page)
	属性
		count对象总数
		num_pages:页面总数
		page_range: 页码列表,从1开始  *
	方法:
		page(整数): 获得一个page对象
			       该方法的返回值类型是Page
	常见错误:
		InvalidPage:page()传递无效页码
		PageNotAnInteger:page()传递的不是整数
		Empty:page()传递的值有效,但是没有数据
		
Page(具体哪一页)
	对象获得
		通过Paginator的page()方法获得
	属性
		object_list:当前页面上所有的数据对象
		number:当前页的页码值
		paginator:当前page关联的Paginator对象
	方法
		has_next():判断是否有下一页
		has_previous():判断是否有上一页
		has_other_pages():判断是否有上一页或下一页
		next_page_number():返回下一页的页码
		previous_page_number():返回上一页的页码
		len():返回当前页的数据的个数		
		
应用场景:
     paginator对象:适用于页码的遍历  eg 上一页 xxx  下一页
     page对象:适用于是否有上一页 、下一页或上一页页码、下一页页码 

创建子路由:

    url(r'^testDjangoPage/',views.testDjangoPage),

生成视图函数:
Paginator对象

def testDjangoPage(request):

    pag = request.GET.get('page',1)
    per_page = request.GET.get('per_page',5)

    student_list = Student.objects.all()

    # Paginator的参数有object_list,per_page
    paginator = Paginator(student_list,per_page)

    # 适用于上一页和下一页之间的页码
    for i in paginator.page_range:
        print(i)
    # 总页数
    print(paginator.num_pages)
    # 一共有多少条(不常用)
    print(paginator.count)

    return HttpResponse('django的分页')

运行结果:
在这里插入图片描述
page对象

def testDjangoPage(request):

    pag = request.GET.get('page',1)
    per_page = request.GET.get('per_page',5)

    student_list = Student.objects.all()

    # Paginator的参数有object_list,per_page
    paginator = Paginator(student_list,per_page)
    
    # page方法返回了一个Page对象
    pa = paginator.page(pag)
    # 判断是否有上一页
    print(pa.has_previous())
    # 判断是否有下一页
    print(pa.has_next())
    # 判断是否有上一页页码
    print(pa.previous_page_number())
    # 判断是否有下一页页码
    print(pa.next_page_number())


    return HttpResponse('django的分页')

运行结果:
在这里插入图片描述

四.富文本

富文本:Rich Text Format(RTF),是有微软开发的跨平台文档格式,大多数的文字处理软件都能读取和保存RTF文档,其实就是可以添加样式的文档,和HTML有很多相似的地方,通常在写论坛,博客时使用的一种带样式的文本插件。
安装插件
    pip install django-tinymce==2.8.0
基本使用
在instatlled_app中添加:tinymce
初始化:
  在settings中注册tinymce应用
  设置默认的配置文件

TINYMCE_DEFAULT_CONFIG = {
                    'theme':'advanced',
                    'width':800,
                    'height':600,
          		}

创建模型:

from tinymce.models import HTMLField
class Blog(models.Model):
    sBlog = HTMLField()

创建子路由:

url(r'^testRTF/',views.testRTF),

生成视图函数:

def testRTF(request):
    if request.method == 'GET':
        return render(request,'testRTF.html')

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>
    <script type="text/javascript">
                       tinyMCE.init({
                              "mode": "textareas",
                              "theme": "advanced",
                              "width": 800,
                              "height": 600
                          })
	</script>
</head>
<body>
    测试富文本
    <form action="" method="post">
        <textarea></textarea>

    </form>
</body>
</html>

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值