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