Django之视图层

JsonResponse

向前端返回一个json格式字符串的两种方式

方式一:

  1. import json
  2. def json_data(request):
  3. user_dict = {'name': 'kevin', 'age': 22, 'hobby': '足球'}
  4. dict_json = json.dumps(user_dict, ensure_ascii=False)
  5. return HttpResponse(dict_json)

方式二:

  1. def json_data(request):
  2. user_dict = {'name': 'kevin', 'age': 22, 'hobby': '足球'}
  3. return JsonResponse(user_dict, safe=False, json_dumps_params={'ensure_ascii': False})
  • 默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
  • 只所以使用JsonResponse是因为djangojson序列化的数据类型的范围做了扩充

form表单上传文件

演示

urls.py

  1. from django.conf.urls import url
  2. from app01 import views
  3. urlpatterns = [
  4. url(r'^register/$', views.register),
  5. ]

view.py

  1. def register(request):
  2. if request.method == 'POST':
  3. name = request.POST.get('name')
  4. header_img = request.FILES.get('header_img')
  5. with open(header_img.name, 'wb') as f:
  6. for line in header_img:
  7. f.write(line)
  8. return HttpResponse('注册成功')
  9. return render(request, 'register.html')

register.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
  8. <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
  9. </head>
  10. <body>
  11. <div class="container">
  12. <div class="row">
  13. <h1 class="text-center">注册</h1>
  14. <div class="col-lg-8 col-md-offset-2">
  15. <form action="" method="post" enctype="multipart/form-data">
  16. {% csrf_token %}
  17. <p>昵称:
  18. <input type="text" class="form-control" name="name">
  19. </p>
  20. <p>头像:
  21. <input type="file" name="header_img" multiple>
  22. </p>
  23. <input type="submit" class="btn btn-success btn-block" value="注册">
  24. </form>
  25. </div>
  26. </div>
  27. </div>
  28. </body>
  29. </html>

总结

form表单上传的数据中如果含有文件 那么需要做以下几件事

  • method必须是post
  • enctype必须修改为multipart/form-data,默认是application/x-www-form-urlencoded
  • 后端需要使用request.FILES获取( django会根据数据类型的不同自动帮你封装到不同的方法中)

request其他方法

  • request.method:获取请求方法
  • request.GET or request.POST:获取GET or POST请求参数,字典形式。
  • request.POST.get('name',default=None):获取POST请求参数
  • request.GET.getlist('name',default=None): 获取GET参数列表
  • request.META:包含当前HTTP请求的Headers头部信息, 字典形式。键值KEY都是大写。比如request.META['REMOTE_ADDR']可获取用户远程IP地址。
  • request.user:获取当前访问用户的所有信息。
  • request.path:获取当前访问路径
  • request.FILES:获取上传文件的数据
  • request.body:获取的是二进制数据(request.POSTrequest.GETrequest.FILES这些获取数据的方法其实都从body中获取数据并解析存放的)
  • request.path_info:获取当前访问路径
  • request.get_full_path():获取路径并且还可以获取到路径后面携带的参数

FBV与CBV

django的视图层由两种形式构成:FBVCBV

FBV

FBV基于函数的视图(Function base view)

urls.py

  1. from django.conf.urls import url
  2. from app01 import views
  3. urlpatterns = [
  4. url(r'^fbv/$',views.fbv),
  5. ]

view.py

  1. from django.shortcuts import render, HttpResponse, redirect
  2. def fbv(request):
  3. return HttpResponse('from FBV')

CBV

CBV基于类的视图(Class base view)

urls.py

  1. from django.conf.urls import url
  2. from app01 import views
  3. urlpatterns = [
  4. url(r'^cbv/$', views.MyIndexView.as_view()),
  5. ]

view.py

  1. from django import views
  2. class MyIndexView(views.View):
  3. def get(self, request):
  4. return HttpResponse('from CBV get view')
  5. def post(self, request):
  6. return HttpResponse('from CBV post view')

运行后的结果是:

  1. 如果请求方式是`GET`则会自动执行类里面的`get`方法;如果请求方式是POST 则会自动执行类里面的`post`方法

CBV源码解析

  1. 通过路由匹配做为切入点,`类名.as_view()`,(函数名加括号执行优先级最高)对象查找的顺序是先从自身,再从产生对象的类,再从父类。先从自己写的`MyIndexView`类中查找`as_view`,没有再去`MyIndexView`类中的父类查找,查看`View`类中找了`as_view`可以看到`Django` `as_vie`方法加了`@classonlymethod`装饰器,作用是只允许类对象调用这个方法(绑定给类的方法)。
  1. @classonlymethod
  2. def as_view(cls, **initkwargs):
  3. """
  4. Main entry point for a request-response process.
  5. """
  6. for key in initkwargs:
  7. if key in cls.http_method_names:
  8. raise TypeError("You tried to pass in the %s method name as a "
  9. "keyword argument to %s(). Don't do that."
  10. % (key, cls.__name__))
  11. if not hasattr(cls, key):
  12. raise TypeError("%s() received an invalid keyword %r. as_view "
  13. "only accepts arguments that are already "
  14. "attributes of the class." % (cls.__name__, key))
  15. def view(request, *args, **kwargs):
  16. self = cls(**initkwargs)
  17. if hasattr(self, 'get') and not hasattr(self, 'head'):
  18. self.head = self.get
  19. self.request = request
  20. self.args = args
  21. self.kwargs = kwargs
  22. return self.dispatch(request, *args, **kwargs)
  23. view.view_class = cls
  24. view.view_initkwargs = initkwargs
  25. # take name and docstring from class
  26. update_wrapper(view, cls, updated=())
  27. # and possible attributes set by decorators
  28. # like csrf_exempt from dispatch
  29. update_wrapper(view, cls.dispatch, assigned=())
  30. return view

可以看到最后返回的是一个return viewview是一个闭包函数(定义在as_view函数内部的函数,并且内部还用到了外部名称空间的名字)。相当于url(r'^cbv/$', views.MyIndexView.as_view()),变成了url(r'^cbv/$', views.view()),可以推导出CBVFBV在路由匹配本质是一样的。

当执行view函数,self = cls(**initkwargs)等同于self = MyIndexView(**initkwargs)意思是产生一个类的对象,最后返回的是一个self.dispatch(request, *args, **kwargs),继续查看dispath函数源码

  1. def dispatch(self, request, *args, **kwargs):
  2. # Try to dispatch to the right method; if a method doesn't exist,
  3. # defer to the error handler. Also defer to the error handler if the
  4. # request method isn't on the approved list.
  5. if request.method.lower() in self.http_method_names:
  6. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  7. else:
  8. handler = self.http_method_not_allowed
  9. return handler(request, *args, **kwargs)

上述也就是CBV核心代码,可以看到返回的是 handlerhandler用了反射中getattr(通过字符串获取对应的变量名或者函数名)

  1. handler = getattr(自己写的类产生的对象, 'get', 当找不到get属性或方法就会用第三个参数)

简单说就是handler = 自己写的类里面的get方法