Loading

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
posted @ 2024-03-25 17:28  HuangQiaoqi  阅读(5)  评论(0编辑  收藏  举报