Django框架之视图层
【一】视图层三板斧
- HttpResponse非常重要,因为三板斧的三个方法返回的都是HttpResponse对象
- 通过看源码可以发现各种各样的类,最终都是由HttpResponse这个类继承而来的
【1】HttpResponse
-
HttpResponse是Django中用于创建HTTP响应的一个类,它可以方便的把一些自定义的文本内容或者一些简单的html页面打包成正确的格式返回给浏览器
-
如果想要返回JSON格式的字符串,django也提供了一个方法,可以快捷的操作,需要导入一个类
-
from django.http import JsonResponse
from django.http import JsonResponse
def test(request):
data = {'name': 'green', 'age': 18, 'hobby': '洗脚'}
return JsonResponse(data, json_dumps_params={'ensure_ascii': False}) ## 如果想要浏览器显示中文 就需要加上这个参数
## 需要注意的是,如果要序列化一个非字典的参数,需要把他的安全模式关掉,要不然就会报错
def test(request):
l1 = [1, 2, 3, 4, 5, 6, 'green大帅比']
return JsonResponse(l1, json_dumps_params={'ensure_ascii': False}, safe=False)
【2】render
-
render通常用于给浏览器返回一个html页面,它支持模板语法。
-
它有一个context参数,默认是None,context的作用就是用来给html传数据的,context是一个大字典
-
也可以直接用local()参数代替,可以把当前视图函数内的所有变量都传递给html页面
from django.shortcuts import render
from .models import MyModel # 假设有一个名为 MyModel 的模型
def detail_view(request, pk):
obj = MyModel.objects.get(pk=pk)
context = {'object': obj} # 将模型实例添加到上下文(context)
return render(request, 'my_template.html', context)
【3】redirect
- redirect函数用于实现网页间的重定向,也就是用户实现从当前url跳转到另外一个url
from django.shortcuts import redirect
def login_required_view(request):
if not request.user.is_authenticated:
return redirect('login') # 重定向到登录页面
# 如果已登录,继续执行后续操作...
【二】form表单文件上传下载
【1】form标签重要参数
<form action="" method="" enctype=""></form>
## action
他的作用是设置提交数据的目标url,如果不填的话默认是向当前url提交
## method
它的作用是设置提交方法,通常由post 和 get 两者,不填的话默认是get请求
## enctype
它的作用是指定在提交表单数据时,浏览器用何中编码类型进行数据编码
通常使用最多的是multipart/form-data,它能将表单数据以二进制方式进行传输,
也就意味着能够传输文件等等,
如果不填参数的话,默认的提交方式是application/x-www-form-urlencoded
【2】上传文件
- 上传文件都是以二进制数据类型传输的,所以要把form标签的enctype属性的值修改为multipart/form-data
- 在视图函数里,获取文件的方法不再是POST,而是FILES,通过
request.FILES.get('标签里面的name值')获得文件数据
- 最后再通过for循环下载文件,将文件保存到本地
def test(request):
if request.method == 'POST':
file_obj = request.FILES.get('file')
with open(file_obj.name, 'wb') as fp:
for line in file_obj:
fp.write(line)
return HttpResponse('ok')
return render(request, 'test.html')
【三】总结request对象方法
## request.method
该方法会返回当前的请求方法,如'POST','GET'等等
## request.POST
该方法返回的是一个类似字典的对象,包含了POST请求所有的参数,通常是由form表单发送的
## request.GET
该方法返回的也是一个类似字典的对象,包含了url问号后面带的所有的参数
## request.FILES
该方法返回的也是一个类似一个字典的对象,包含了所有文件信息
## request.path
该方法会返回请求的url
## request.get_full_path()
该函数也会返回url地址,但是它还能返回参数部分,也就是问号后面的部分
【四】FBV与CBV
- 视图层里面不仅可以写函数与url对应关系,还可以写类与url对应关系
- 通过函数与url对应的就是FBV
- 通过类与url对应的就是CBV
【1】FBV
## 视图函数
def login(requests):
if requests.method == 'POST':
# 获取用户数据
username = requests.POST.get('username')
password = requests.POST.get('password')
user_obj = models.User.objects.filter(username=username)
if not user_obj:
messages.error(requests, f'用户{username}未注册')
return redirect('/login/')
if password != user_obj.first().password:
messages.error(requests, '密码错误')
return redirect('/login/')
return redirect('/user_list/')
return render(requests, 'login.html')
【2】CBV
- 用类来写功能首先要导入一个父类
from django.views import View
- 然后定义一个继承View类的类
- 要重写两个方法,get和post,当浏览器输入对应的url时,django会自动实例化出来一个这个类的对象
- 在View类内部会判断当前请求的方法,如果是GET请求,就会调用get方法,反之则调用post方法
## 视图类
class LoginView(View):
def get(self, request):
return render(self.request, 'login.html')
def post(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
if not username:
return HttpResponse('用户名不可为空')
if not password:
return HttpResponse('密码不可为空')
user_obj = models.Student.objects.filter(username=username)
if not user_obj:
return HttpResponse(f'用户{username}暂未注册')
return redirect('/app1/user_info/')
## urls.py
from django.contrib import admin
from django.urls import path, re_path
from app1 import views
urlpatterns = [
path('login/', views.LoginView.as_view()),
]
【3】CBV源码解析
class View:
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
'''
首先看as_view这个方法,仔细观察可以看出这是一个闭包函数,调用as_view相当于调用其内部的
view
'''
@classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'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 view(request, *args, **kwargs):
'''
这里的cls指的就是我们自己定义的类
'''
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
return view
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
def dispatch(self, request, *args, **kwargs):
'''
判断当前请求方法,以GET为例
handler就是get
最后就是调用get方法
'''
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)
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb."""
response = HttpResponse()
response.headers['Allow'] = ', '.join(self._allowed_methods())
response.headers['Content-Length'] = '0'
return response
def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
- 首先点进去as_view查看源码,会发现这是一个闭包函数,
- 调用as_view就相当于调用view,
- view最后返回了dispatch函数,相当于又执行了dispatch函数
- handler会返回当前请求类型,最后加括号调用,就相当于自动执行了我们自己写的类里面的post或者get方法。
【五】给视图装装饰器
【1】使用装饰器装饰FBV
- FBV本身就是一个函数,所以和给普通的函数加装饰器无差:
def wrapper(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print("used:", end_time-start_time)
return ret
return inner
# FBV版添加班级
@wrapper
def add_class(request):
if request.method == "POST":
class_name = request.POST.get("class_name")
models.Classes.objects.create(name=class_name)
return redirect("/class_list/")
return render(request, "add_class.html")
【2】使用装饰器装饰CBV
- 由于这是类里面的方法,所以不能直接加装饰器
- 但是django给我们提供了方法,首先需要导入模块
from django.utils.decorators import method_decorator
- 然后就像有参装饰器一样给类里面的方法加装饰器即可
# CBV版添加班级
from django.views import View
from django.utils.decorators import method_decorator
def wrapper(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print("used:", end_time-start_time)
return ret
return inner
class AddClass(View):
@method_decorator(wrapper)
def get(self, request):
return render(request, "add_class.html")
def post(self, request):
class_name = request.POST.get("class_name")
models.Classes.objects.create(name=class_name)
return redirect("/class_list/")
【3】通过重写dispatch方法
- 通过观察as_view的源码可以发现,它内部在经历一系列的校验,最后是通过dispatch方法调用当前的请求方法的
- 所以就可以根据这一特性,重写dispatch方法,在dispatch方法里面写其他的功能,以此来达到装饰器增加功能的效果
class LoginView(View):
def get(self, request):
return render(self.request, 'login.html')
def post(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
if not username:
return HttpResponse('用户名不可为空')
if not password:
return HttpResponse('密码不可为空')
user_obj = models.Student.objects.filter(username=username)
if not user_obj:
return HttpResponse(f'用户{username}暂未注册')
return redirect('/app1/user_info/')
def dispatch(self, request, *args, **kwargs):
print('start')
res = super().dispatch(request, *args, **kwargs)
print('end')
return res