类视图和中间件

05-django入门-类视图和中间件

1类视图

1.1类视图概念引入

视图函数与类视图的区别

以函数的方式定义的视图称为函数视图,函数视图便于理解。
但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,
便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。

  • 1.1案例使用:
def register(request):
    """处理注册"""

    # 获取请求方法,判断是GET/POST请求
    if request.method == 'GET':
        # 处理GET请求,返回注册页面
        return render(request, 'register.html')
    else:
        # 处理POST请求,实现注册逻辑
        return HttpResponse('这里实现注册逻辑')

url
    path('index2/',views.index2 ),

  
class RegisterView(View):
    """
    这是定义类视图
    """
    # 这个类里面定义的方法不是自定义的是请求方法
    def get(self, request):

        return render(request,'index3.html')

    def post(self, request):
        # 获取用户名
        username = request.POST.get('username')
        print(username)
        from django.http import JsonResponse
        return JsonResponse({'username':username})


register.html 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="#" method="post">
    username: <input  type="text" id="username" name="username"><br>
    password: <input type="password" id="password" name="password" ><br>
    register: <button type="submit">注册</button>
    register: <button  type="submit" ><a href="{% url 'index3' %}" >注册</a> </button>
    
</form>


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

  • 1.2使用类视图定义函数

    在 django中使用定义类的方式来定义一个视图 ,称之为类视图
    使用类视图可以将视图对应的不同请求方式以类中不同方法来区别定义
    类视图的好处:
    
    代码可读性好
    类视图相对于函数视图有更高的复用性, 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可.
    
    from django.views.generic import View
    
    class RegisterView(View):
        """类视图:处理注册"""
    
        def get(self, request):
            """处理GET请求,返回注册页面"""
            return render(request, 'register.html')
    
        def post(self, request):
            """处理POST请求,实现注册逻辑"""
            return HttpResponse('这里实现注册逻辑')
    
    urlpatterns = [
        # 视图函数:注册
        # path('register/', views.register, name='register'),
        # 类视图:注册
        path('register/', views.RegisterView.as_view(), name='register'),
    ]
    

- 1.3类视图的原理

```Python
@classonlymethod
  def as_view(cls, **initkwargs):
      """
      Main entry point for a request-response process.
      """
      ...省略代码...

      def view(request, *args, **kwargs):
          self = cls(**initkwargs)
          if hasattr(self, 'get') and not hasattr(self, 'head'):
              self.head = self.get
          self.request = request
          self.args = args
          self.kwargs = kwargs
          # 调用dispatch方法,按照不同请求方式调用不同请求方法
          return self.dispatch(request, *args, **kwargs)

      ...省略代码...

      # 返回真正的函数视图
      return view


  def dispatch(self, request, *args, **kwargs):
      # Try to dispatch to the right method; if a method doesn't exist,
      # defer to the error handler. Also defer to the error handler if the
      # request method isn't on the approved list.
      if request.method.lower() in self.http_method_names:
          handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
      else:
          handler = self.http_method_not_allowed
      return handler(request, *args, **kwargs)
  • 1.4扩展(不需要掌握 )
  • 类视图使用装饰器
  • 自定义一个装饰器用来做调用
def my_decorator(func):
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了')
        print('请求路径%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

class DemoView(View):
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')

    def post(self, request):
        print('post方法')
        return HttpResponse('ok')
        
# 此方法为在URL配置中装饰, 此种方式会为类视图中的所有请求方法都加上装饰器行为        
urlpatterns = [
    path('demo/', my_decorate(DemoView.as_view()))
] 
# 在类视图中装饰,在类视图中使用为函数视图准备的装饰器时,不能直接添加装饰器,需要使用method_decorator将其转换为适用于类视图方法的装饰器


# 为全部请求方法添加装饰器
from django.utils.decorators import method_decorator
class DemoView(View):

    @method_decorator(my_decorator) 
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)
  
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')

    def post(self, request):
        print('post方法')
        return HttpResponse('ok')

from django.utils.decorators import method_decorator
# 为特定请求方法添加装饰器
class DemoView(View):

    @method_decorator(my_decorator)
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')

    def post(self, request):
        print('post方法')
        return HttpResponse('ok')

为什么需要使用method_decorator呢
为函数视图准备的装饰器,其被调用时,第一个参数用于接收request对象,
而类视图中请求方法被调用时,传入的第一个参数不是request对象,
而是self 视图对象本身,第二个位置参数才是request对象
所以如果直接将用于函数视图的装饰器装饰类视图方法,会导致参数传递出现问题。

method_decorator的作用是为函数视图装饰器补充第一个self参数,以适配类视图方法。

2中间件

什么是中间件?
Django中的中间件是一个轻量级、底层的插件系统
可以用来做什么?
Django 中间件是修改 Django request 或者 response 对象的钩子,可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。


传话人,中间商。能够介入请求与响应的流程之间,可以对请求对象或者响应对象进行修改,修改了之后在传到下一个流程之中进程处理;同时中间件也可以提升项目的一个安全性。同时中间件的作用范围是整个请求响应的流程。

**调用的过程->看做递归的形式->当中间件在配置文件中的中间件列表中不是最后一个元素的时候,此时get_response为指向下一个中间件;当中间件在配置文件中的中间件列表中是最后一个元素的时候,此时get_response可直接看做一个视图**

自定义中间件:以闭包的形式存在



##### 中间件开发流程:

1. 在项目中创建一个用于存放中间件代码的文件夹
2. 在此文件夹中进行中间件的设计
3. 中间设计好了之后,需要将自定义的中间件注册到项目之中
  • 自带中间件介绍

    # django中的中间件     底层的插件系统
    MIDDLEWARE = [
        # 主要是针对安全访问处理,就是把http请求重定向到https请求
        'django.middleware.security.SecurityMiddleware',
        # 在Django中我们用的request.session就是在process_request中进行处理的,
        # 根据我们在settings中配置的SESSION_COOKIE_NAME变量,
        # 从cookies中获取对应的值,从表中查询出session值,创建session对象,
        # 赋值给request_session对象。
        'django.contrib.sessions.middleware.SessionMiddleware',
        # 检测是否允许浏览器类型
        'django.middleware.common.CommonMiddleware',
        #跨域请求伪造中间件。加入这个中间件,在提交表单的时候会必须加入csrf_token,cookie中也会生成一个名叫csrftoken的值,也会在header中加入一个HTTP_X_CSRFTOKEN的值来放置CSRF攻击
        # 'django.middleware.csrf.CsrfViewMiddleware',
        
        # 从用户表中查询对应主键,得到用户对象,将其付给request.user
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        # Django的消息框架,主要是向目标中推送消息内容
        'django.contrib.messages.middleware.MessageMiddleware',
        # 防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    
        # 中间件的执行顺序 先调用最下面的中间件,再先从上往下执行,再从下往上
    ]
    
  • 2.1案例自定义中间件
1在子应用文件下新建(自定义).py文件(middlewarse.py)
# 1在子应用文件下创建(自定义).py文件
# 2 自定义中间件
def outer1(fuc):
    print('调用前1')
    def inner(*args,**kwargs):
        print('执行前1')
        data = fuc(*args,**kwargs)
        print('执行后1')
        return data
    return inner
def outer2(fuc):
    print('调用前2')
    def inner(*args,**kwargs):
        print('执行前2')
        data = fuc(*args,**kwargs)
        print('执行后2')
        return data
    return inner

def outer3(fuc):
    print('调用前3')
    def inner(*args,**kwargs):
        print('执行前3')
        data = fuc(*args,**kwargs)
        print('执行后3')
        return data
    return inner
  • 2.2 在setting.py中的MIDDLEWARE[] 中添加中间件的路径
 'users.middlewarse.outer1',     # 自定义中间件
 'users.middlewarse.outer2',     # 自定义中间件
 'users.middlewarse.outer3',     # 自定义中间件
中间件可以定义四个方法,分别是:

process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_response(self, request, response)

from django.utils.deprecation import MiddlewareMixin
process_request 方法
process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。

process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。

返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
process_request 方法是在视图函数之前执行的。

当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。

不同中间件之间传递的 request 参数都是同一个请求对象。

process_response 方法有两个参数,一个是 request,一个是 response,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。

process_response 方法是在视图函数之后执行的。

当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行

process_view 方法格式如下:

process_view(request, view_func, view_args, view_kwargs) process_view 方法有四个参数:
request 是 HttpRequest 对象。
view_func 是 Django 即将使用的视图函数。
view_args 是将传递给视图的位置参数的列表。
view_kwargs 是将传递给视图的关键字参数的字典。
view_args 和 view_kwargs 都不包含第一个视图参数(request)。

process_view 方法是在视图函数之前,process_request 方法之后执行的。


process_exception 方法如下:

process_exception(request, exception)
参数说明:

request 是 HttpRequest 对象。
exception 是视图函数异常产生的 Exception 对象。
process_exception 方法只有在视图函数中出现异常了才执行,按照 settings 的注册倒序执行。

在视图函数之后,在 process_response 方法之前执行。

process_exception 方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。

返回值是 None,页面会报 500 状态码错误,视图函数不会执行。


class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("md1  process_request 方法。", id(request)) #在视图之前执行

    def process_response(self,request, response): :#基于请求响应
        print("md1  process_response 方法!", id(request)) #在视图之后
        return response

    def process_view(self,request, view_func, view_args, view_kwargs):
        print("md1  process_view 方法!") #在视图之前执行 顺序执行
        #return view_func(request)


    def process_exception(self, request, exception):#引发错误 才会触发这个方法
        print("md1  process_exception 方法!") 
        # return HttpResponse(exception) #返回错误信息
        
 中间件应用场景
1、做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

2、URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!
from django.utils.deprecation import MiddlewareMixin
from django .http import HttpResponse

white = ['127.0.0.1']   # 白名单
black = ['127.0.0.2']   # 黑名单
ban = {}                # 小黑屋
ban_seconds = 3         # 三秒内
ban_limit = 5           # 三秒内允许访问5次
ban_time = 30           # 被封时间30秒

class AuthMD(MiddlewareMixin):
    def process_request(self,request):
        ip = str(request.META.get("REMOTE_ADDR"))
        if ip in black:
            return HttpResponse('禁止访问',status=403)
        # 如果是第一次请求
        if not ban.get(ip):
            ban[ip] = {"total":1,"time":int(time.time()),"banTime":""}
        print(ip,ban[ip].get("total"))
        # 如果不是第一次请求 则判断上次请求和这次请求是否在合法时间内
        if ban[ip]["time"] + ban_seconds > int(time.time()):
            # 如果是三秒内 判断访问次数
            if ban[ip]["total"] > ban_limit:
                # 如果访问次数大于限制次数 直接return 将ip关小黑屋 设置限制时间
                ban[ip]["banTime"] = int(time.time()) + ban_time
                return self.ban_response()
            # 如果没有大于限制次数
            ban[ip]["total"] += 1
            print(ban)
        # 如果不是在合法时间内请求
        else:
            # 先根据此ip找封的时间
            limit_time = ban[ip]["banTime"]
            # 如果已经禁止,则过ban_seconds秒后才可以解除
            if limit_time and limit_time > int(time.time()):
                return self.ban_response()
            # 过了限制时间,解除限制
            del ban[ip]

    def ban_response(self):
        return HttpResponse(f"访问过于频繁,请{ban_time}秒后重试!")

    def process_response(self,request,response):
        return response

3.模板配置和模板语法

1.在django根目录下创建模板文件夹用来存放模板文件 (template)
2.settings.py中的TEMPLATES里的DIRS配置模板路径
3.使用模板,在文件夹下创建html文件


index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<a href="#" >{{ name|cut:"" | lower |capfirst }}老师</a><br>
<a href="#" >性别:{{ gender }}</a><br>
<a href="#" >年龄:{{ age}}</a><br>
<a href="#" >num1  {{ num1|add:num2 }}</a><br>
<a href="#" >num2  {{ num2| add:num1}}</a><br>

</body>
</html>

在views中编写

def test4(request):
    name1 = 'q    dsfadgRERETD'
    gender = '男'
    age = 34
    num1 = 1
    num2 = 2

    context = {
        'name': name1,
        'gender': gender,
        'age': age,
        'num1': num1,
        'num2':num2
    }

    return render(request, 'index.html',context=context)

6总结

  • 1.掌握类视图的使用
  • 2.掌握类视图的使用场景
  • 3.了解中间件的流程
    1. 掌握模板的设置和使用
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容