Django之视图层
JsonResponse
向前端返回一个json
格式字符串的两种方式
方式一:
import json
def json_data(request):
user_dict = {'name': 'kevin', 'age': 22, 'hobby': '足球'}
dict_json = json.dumps(user_dict, ensure_ascii=False)
return HttpResponse(dict_json)
方式二:
def json_data(request):
user_dict = {'name': 'kevin', 'age': 22, 'hobby': '足球'}
return JsonResponse(user_dict, safe=False, json_dumps_params={'ensure_ascii': False})
- 默认
safe=True
代表只能序列化字典对象,safe=False
代表可以序列化字典以外的对象 - 只所以使用
JsonResponse
是因为django
对json
序列化的数据类型的范围做了扩充
form表单上传文件
演示
urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^register/$', views.register),
]
view.py
def register(request):
if request.method == 'POST':
name = request.POST.get('name')
header_img = request.FILES.get('header_img')
with open(header_img.name, 'wb') as f:
for line in header_img:
f.write(line)
return HttpResponse('注册成功')
return render(request, 'register.html')
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<h1 class="text-center">注册</h1>
<div class="col-lg-8 col-md-offset-2">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>昵称:
<input type="text" class="form-control" name="name">
</p>
<p>头像:
<input type="file" name="header_img" multiple>
</p>
<input type="submit" class="btn btn-success btn-block" value="注册">
</form>
</div>
</div>
</div>
</body>
</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.POST
、request.GET
、request.FILES
这些获取数据的方法其实都从body
中获取数据并解析存放的)request.path_info
:获取当前访问路径request.get_full_path()
:获取路径并且还可以获取到路径后面携带的参数
FBV与CBV
django
的视图层由两种形式构成:FBV
和CBV
FBV
FBV基于函数的视图(Function base view)
urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^fbv/$',views.fbv),
]
view.py
from django.shortcuts import render, HttpResponse, redirect
def fbv(request):
return HttpResponse('from FBV')
CBV
CBV基于类的视图(Class base view)
urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^cbv/$', views.MyIndexView.as_view()),
]
view.py
from django import views
class MyIndexView(views.View):
def get(self, request):
return HttpResponse('from CBV get view')
def post(self, request):
return HttpResponse('from CBV post view')
运行后的结果是:
如果请求方式是`GET`则会自动执行类里面的`get`方法;如果请求方式是POST 则会自动执行类里面的`post`方法
CBV源码解析
通过路由匹配做为切入点,`类名.as_view()`,(函数名加括号执行优先级最高)对象查找的顺序是先从自身,再从产生对象的类,再从父类。先从自己写的`MyIndexView`类中查找`as_view`,没有再去`MyIndexView`类中的父类查找,查看`View`类中找了`as_view`可以看到`Django` 给`as_vie`方法加了`@classonlymethod`装饰器,作用是只允许类对象调用这个方法(绑定给类的方法)。
@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("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (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):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
可以看到最后返回的是一个return view
,view
是一个闭包函数(定义在as_view
函数内部的函数,并且内部还用到了外部名称空间的名字)。相当于url(r'^cbv/$', views.MyIndexView.as_view()),
变成了url(r'^cbv/$', views.view()),
可以推导出CBV
与FBV
在路由匹配本质是一样的。
当执行view
函数,self = cls(**initkwargs)
等同于self = MyIndexView(**initkwargs)
意思是产生一个类的对象,最后返回的是一个self.dispatch(request, *args, **kwargs)
,继续查看dispath
函数源码
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)
上述也就是CBV核心代码,可以看到返回的是 handler
,handler
用了反射中getattr
(通过字符串获取对应的变量名或者函数名)
handler = getattr(自己写的类产生的对象, 'get', 当找不到get属性或方法就会用第三个参数)
简单说就是handler = 自己写的类里面的get方法