Python入门自学进阶-Web框架——10Django-应用COOKIESESSION和装饰器,及FBVCBV概念

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——10Django-应用COOKIESESSION和装饰器,及FBVCBV概念相关的知识,希望对你有一定的参考价值。

一、使用cookie和session进行用户验证。

def login(req):
    message =""
    if req.method == "POST":
        user = req.POST.get('user')
        pwd = req.POST.get('pwd')
        counts = models.Administrator.objects.filter(username=user,password=pwd).count()
        if counts:
            req.session['is_login'] = True
            req.session['username'] = user
            rep = redirect('index.html')
            return rep
        else:
            message = "用户名或密码错误"
    return render(req,"login.html",'msg':message)
def index(req):
    current_user = req.session.get('username')
    if current_user :
        return render(req,"index.html",'username':current_user)
    else:
        return redirect('login.html')

index视图函数进行了判断,如果用户不存在,就重定向到login页面。这主要是用于防止直接访问index.html,而不经过login.html的非法访问。用户存在,在session中进行保存相关信息。一般的,对于每一个需要登陆以后才能访问的页面,都需要进行判断用户是否登陆,这样的话,每个视图函数的这个功能都是重复的,所以要想办法抽出出来。简化代码。

解决的方法是使用前面学过的装饰器。装饰器的一个作用就是在执行一个函数时,额外增加执行一些功能,这里就是在执行对应的视图函数时,先检查是否登录。

定义用于验证登录的装饰器函数

def auth(func):
    def inner(req,*args,**kwargs):
        is_login = req.session.get('is_login')
        if is_login:
            return func(req,*args,**kwargs)
        else:
            return redirect('login.html')
    return inner

@auth
def index(req):
    current_user = req.session.get('username')
    return render(req,"index.html",'username':current_user)

装饰器auth可以加在任何一个需要验证登录后才能访问的视图函数,简化了代码。

退出登录:就是清除session,下一次登录时就必须重新输入用户和密码,否则下次直接访问index.html,在session的有效期内会直接进入。

视图函数:

def logout(req):
    req.session.clear()
    return redirect("login.html")

路由项:

from django.contrib import admin
from django.urls import path
from myadminzdy import views

urlpatterns = [
    path('test/', views.test),
    path('login.html',views.login),
    path('index.html',views.index),
    path('logout.html',views.logout),
]

前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>hello  username </h1>
    <hr>
    <a href="logout.html">注销</a>
</body>
</html>

以上就是使用cookie、session和装饰器进行验证的大体流程。注意,session是借助于cookie的,所以上述代码没有显式使用cookie。

二、关于FBV和CBV

前面学习的django中的路由项使用的是:

url  ——> def 函数     来定义的路由项,这叫做FBV(Function Based Views),即视图依赖于函数。

其实还有一种,即使用类来实现视图的功能,即

url  ——>class 类  这叫做CBV(Class Based Views)

测试代码:

from django.shortcuts import render,HttpResponse,redirect
from myadminzdy import models
from django import views
from django.contrib.sessions.backends.db import SessionStore
# Create your views here.

class Login(views.View):
    def get(self,req,*args,**kwargs):
        return render(req,'login.html','msg':'')
    def post(self,req,*args,**kwargs):
        user = req.POST.get('user')
        pwd = req.POST.get('pwd')
        counts = models.Administrator.objects.filter(username=user, password=pwd).count()
        if counts:
            req.session['is_login'] = True
            req.session['username'] = user
            rep = redirect('index.html')
            return rep
        else:
            message = "用户名或密码错误"
        return render(req, "login.html", 'msg': message)

首先需要引入django的view模块,这个模块中有一个http_method_names,如下:

 说明有这么多的方法,就可以重写这些方法,如上面的常用的get和post方法。

在类中就不需要在进行方法的判定,直接就进入相应的方法中,逻辑比较明晰。

关于路由项的写法:

urlpatterns = [
    path('test/', views.test),
    # path('login.html',views.login),
    path('login.html',views.Login.as_view()),  # 类名后跟as_view()方法
    path('index.html',views.index),
    path('logout.html',views.logout),
]

主要区别就是视图函数要写类的as_view()方法。

那么对于类,如果要使用装饰器,怎么使用呢?

首先定义装饰器,这与以前没有什么不同,关键是装饰器的使用,需要在类的方法上使用,并且需要使用@method_decorator(装饰器)来使用,method_decorator需要引入。
 

from django.shortcuts import render,HttpResponse,redirect
from myadminzdy import models
from django import views
from django.contrib.sessions.backends.db import SessionStore
from django.utils.decorators import method_decorator   # 类的方法上使用装饰器,要使用此函数
# Create your views here.

# 定义装饰器outer
def outer(func):
    def inner(req,*args,**kwargs):
        print('装饰器功能...本次的请求方法:',req.method)
        return func(req,*args,**kwargs)
    return inner

class Login(views.View):

    @method_decorator(outer)        # get方法使用装饰器
    def get(self,req,*args,**kwargs):
        print('Class get')
        return render(req,'login.html','msg':'')

    @method_decorator(outer)     # post方法使用装饰器
    def post(self,req,*args,**kwargs):
        print('Class post')
        user = req.POST.get('user')
        pwd = req.POST.get('pwd')
        counts = models.Administrator.objects.filter(username=user, password=pwd).count()
        if counts:
            req.session['is_login'] = True
            req.session['username'] = user
            rep = redirect('index.html')
            return rep
        else:
            message = "用户名或密码错误"
        return render(req, "login.html", 'msg': message)

以上是类方法上使用装饰器的方法。

运行结果,先访问login.html(get),然后提交登录用户名密码登录(post)

前面看到http_method_names中保存了很多方法名字的列表,每个名字都对应于一个类方法,对于浏览器发送过来的request请求,都是有一个请求头的,请求头中包含了method键值对,定义了本次请求的方法,其值是一个大写的字符串,我们定义的视图类会取得这个方法字符串,进行处理后,使用反射,变为对特定类方法的调用。相当于一个分配器功能。查看View的源代码,可以看到如下:

 @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError(
                    'The method name %s is not accepted as a keyword argument '
                    'to %s().' % (key, cls.__name__)
                )
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))



    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)

可以看到,as_view()方法中对方法名进行验证,判断是否在http_method_names,即如果我们要自定义自己的HTTP提交方法,需要在http_method_names这里添加上。

然后是dispatch方法,就是分配器,先对请求头中的方法字符串进行小写,然后反射成对应的方法进行调用。也就是说,请求到来后,在进入Login类视图中时先执行的是dispatch方法,我们可以重写这个方法,添加自己的逻辑:

    def dispatch(self, request, *args, **kwargs):
        print(111111)
        # 调用父类中的dispatch方法
        ret = super(Login,self).dispatch(request, *args, **kwargs)
        print(222222)
        return ret

运行结果,后台打印:

先执行dispatch,打印111111,后执行dispatch的父类dispatch方法,此方法返回的是一个HttpResponse对象,看前面的父类中源代码的dispatch,:

handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
return handler(request, *args, **kwargs)

第一次请求login.html,handler其实就是get,返回的是get(request, *args, **kwargs),再看我们写的get方法,其运行结果是返回一个render(),即return render(),我们知道,最终结果是一个HttpResponse对象,然后返回给浏览器,因为这里是我们的dispatch调用的,所以结果返回给了我们自定义的dispatch,我们自定义的dispatch还需要接收这个返回结果,在将其返回,即最后要有return ret,否则会出现如下错误:

 根据前面的分析,我们可以看到,其实dispatch有点像装饰器的功能,可以在执行相应的方法前后执行一些额外的功能,这样只需要在dispatch上定义这些功能。这样类中的其他方法就不需要用装饰器,将装饰器的功能写在dispatch中。

在dispatch中写装饰器的功能,可以节省给类中其他方法加装饰器,但是如果是多个类呢?如我们有很多页面,每个页面都对应一个类,那每个类都要重写dispatch方法,又有些代码重复了,这时可以将dispatch与装饰器结合,即装饰器只加在dispatch上就可以了:

第一种方法是在dispatch方法上加@method_decorator(outer) :

def outer(func):
    def inner(req,*args,**kwargs):
        print('装饰器功能...本次的请求方法:',req.method)
        return func(req,*args,**kwargs)
    return inner

class Login(views.View):
    @method_decorator(outer)            
    def dispatch(self, request, *args, **kwargs):
        print("dispatch:",111111)
        # 调用父类中的dispatch方法
        ret = super(Login,self).dispatch(request, *args, **kwargs)
        print("dispatch:",222222)
        return ret

    def get(self,req,*args,**kwargs):
        print('Class get')
        return render(req,'login.html','msg':'')

    def post(self,req,*args,**kwargs):
        print('Class post')
        user = req.POST.get('user')
        pwd = req.POST.get('pwd')
        counts = models.Administrator.objects.filter(username=user, password=pwd).count()
        if counts:
            req.session['is_login'] = True
            req.session['username'] = user
            rep = redirect('index.html')
            return rep
        else:
            message = "用户名或密码错误"
        return render(req, "login.html", 'msg': message)

class Index(views.View):
    @method_decorator(outer)
    def dispatch(self, request, *args, **kwargs):
        return super(Index,self).dispatch(request, *args, **kwargs)
    
    def get(self,req,*args,**kwargs):
        pass
    def post(self,req,*args,**kwargs):
        pass


class Index2(views.View):
    @method_decorator(outer)
    def dispatch(self, request, *args, **kwargs):
        return super(Index2, self).dispatch(request, *args, **kwargs)

    def get(self, req, *args, **kwargs):
        pass

    def post(self, req, *args, **kwargs):
        pass
  

第二种方法是在类上面加装饰器:

@method_decorator(outer,name='dispatch')  # 类方法上的装饰器使用的第二种方法
# @method_decorator(outer,name='get')       # 可以加多个装饰器
class Login(views.View):
    # @method_decorator(outer)        # 类方法上的装饰器使用的第一种方法
    def dispatch(self, request, *args, **kwargs):
        print("dispatch:",111111)
        # 调用父类中的dispatch方法
        ret = super(Login,self).dispatch(request, *args, **kwargs)
        print("dispatch:",222222)
        return ret

    def get(self,req,*args,**kwargs):
        print('Class get')
        return render(req,'login.html','msg':'')

    def post(self,req,*args,**kwargs):
        print('Class post')
        user = req.POST.get('user')
        pwd = req.POST.get('pwd')
        counts = models.Administrator.objects.filter(username=user, password=pwd).count()
        if counts:
            req.session['is_login'] = True
            req.session['username'] = user
            rep = redirect('index.html')
            return rep
        else:
            message = "用户名或密码错误"
        return render(req, "login.html", 'msg': message)

如果对某一种请求做处理:在相应请求方法上加单一装饰器
如果对所有请求做处理:在dispatch方法上加单一装饰器

以上是关于Python入门自学进阶-Web框架——10Django-应用COOKIESESSION和装饰器,及FBVCBV概念的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——21DjangoAdmin项目应用