Django04

零:Django视图层

Django视图层相关的知识都是在APP下的views.py中所应用的,视图层的主要功能就是如何处理由路由层解析过来的request请求,主要包含以下几块内容

1.如何处理request请求对象

2.如何返回一个HTML文档或其他内容给前端页面

3.如何对接上数据库实时获取数据

一:请求对象

这里的request是django封装的对象,它的类是WSGIRequest,它里面包含了所有http请求的东西

print(request)          # <WSGIRequest: GET '/upload/'>
print(type(request))    # <class 'django.core.handlers.wsgi.WSGIRequest'>

WSGIRequest继承了http的HttpRequest

image-20201012073209270

HttpRequest中,内置了许多方法

请求对象的方法

request.method - 返回请求的方法
print(request.method)		# GET、POST等
request.GET - QueryDict对象,保存了用户提交过来的数据(不包含文件)
http://127.0.0.1:8000/index/?name=Darker&age=18 	# 手动在地址栏填入的数据

print(request.GET)		# <QueryDict: {'name': ['Darker'], 'age': ['18']}>
request.POST - QueryDict对象,保存了用户提交过来的数据(不包含文件)
http://127.0.0.1:8000/index/?name=Ben&gender=male 	# 手动在地址栏填入的数据

print(request.POST)		# <QueryDict: {'name': ['Ben'], 'gender': ['male']}>
request.is_ajax() - 判断是不是ajax请求
print(request.is_ajax())		# True / False
request.path - 返回当前访问的路径
http://127.0.0.1:8000/index/?name=Darker&age=18

print(request.path)		# /index/
request.get_full_path() - 返回当前访问的完整路径
http://127.0.0.1:8000/index/?name=Darker&age=18

print(request.get_full_path())		# /index/?name=Darker&age=18
request.encoding - 客户端向服务端传递时,使用的编码方法
print(request.encoding)		# urlencoded / form-data / json
request.META - *重要 | 包括了很多信息

request.META中的信息

print(request.META['SERVER_PORT'])      # 服务的端口  8080
print(request.META['SERVER_PROTOCOL'])  # 服务的HTTP协议  HTTP/1.1
print(request.META['REMOTE_ADDR'])      # 访问的设备IP   127.0.0.1
print(request.META['CONTENT_TYPE'])     # 客户端向服务端发送的编码方式    text/plain
print(request.META['HTTP_HOST'])        # 主机IP  127.0.0.1:8080
print(request.META['HTTP_USER_AGENT'])  # 访问设备的浏览器类型    Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0
print(request.META['HTTP_REFERER'])     # 访问的前一个URL http://127.0.0.1:8080/index/
request.FILES - 用户上传的文件

二:响应对象

HttpResponse - 接收一个字符串并返回

from django.shortcuts import HttpResponse

def return_HttpResponse(request):
    return HttpResponse("Hello World")

render

render()有多个参数,其中第一个参数为返回request请求对象,其他参数则用于返回HTML文档及其局部命名空间内变量用作模板渲染。

参数 内容
request 用于生成响应的请求对象
template_name 要使用的模板的完整名称,可选的参数
context 添加到模板上下文的一个字典
默认是一个空字典
如果字典中的某个值是可调用的,视图将在渲染模板之前调用它
局部命名空间变量(字典形式从换入),或locals()函数
from django.shortcuts import render

def return_render(request):

    user_msg = {"name":"Darker","age":18,"gender":"male"}

    return render(request,"login.html",{"user_msg":user_msg}) 

    # 参数3:{"user_msg":user_msg}  只传递user_msg到HTML文档
    # 参数3:locals() 将当前函数的命名空间(return_render)下的所有变量传递到HTML文档

redirect - 接收一个url并返回,状态码为302,即重定向

from django.shortcuts import redirect

def return_redirect(request):
    return redirect("http://www.xuexianqi.top") # 跳转到www.xuexianqi.top | 这里的参数可以是一个路由,也可以是一个完整的URL

JsonResponse

JsonresponseDjango中自带的一个基于json模块的封装,可以直接返回json类型的数据至模板层的前端页面。

1.只支持字典,如果想传递其他数据类型需要将safe设置为False

2.如果想让转换后的json格式字符串中存在中文,请设置参数json_dumps_params={"ensure_ascii":False},即关闭自动转码

from django.http import JsonResponse

def f1(request):
    data = {"name":"Darker","age":18}
    return JsonResponse(data,json_dumps_params={"ensure_ascii":False})  # 只支持字典,关闭自动转码

本质

redirectrenderJsonResponse的本质,都是HttpResponse

# 这里以render为例,用HttpResponse来实现

def f1(request):
    data = {"name":"Darker","age":18}

    from django.template import Template,Context
    
    res = Template("<h1>{{user}}<h1>")  # 模板,将HTML代码渲染成模板
    con = Context({"user":{"name":"Darker"}}) # 替换的内容封装成Context对象
    ret = res.render(con) # 执行替换
    
    return HttpResponse(ret)

三:CBV与FBV

FBV - Function Based View:基于函数的视图

FBV是在视图层里通过函数的方式进行逻辑处理。

具体用法:

# urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('^index/', views.index)  # 这里的index后面不能加括号
]
# views.py

from django.shortcuts import HttpResponse

def index(request):
    if request.method == 'POST':
        return HttpResponse("这是POST请求")
    else:
        return HttpResponse("这是GET请求")

CBV - Class Based View:基于类的视图

CBV是在视图层里通过类的方式进行逻辑处理。

具体用法:

# urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('^index/', views.Index.as_view())  # 这里的as_view后面需要加上括号
]
# views.py

from django.shortcuts import HttpResponse
from django.views import View # 导入View

class Index(View): # 必须继承View
 
    def get(self,request):  # 当get请求来,执行该方法
        return HttpResponse("这是GET请求")

    def post(self,request): # 当post请求来,执行该方法
        return HttpResponse("这是POST请求")

CBV源码分析

@classonlymethod  # django封装的类方法,只能用于装饰class(类)
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):  # 这是1个闭包函数,此时不会运行
        self = cls(**initkwargs)  # self=Index,这里的Index就是自己在views.py中定义的类
        if hasattr(self, 'get') and not hasattr(self, 'head'):  # 如果有Index类有get方法并且没有head方法
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)  # 将request对象传进去, 执行dispatch,并返回

    view.view_class = cls  # view.view_class = Index
    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=())  # 这里的cls.dispatch就是Index.dispatch,但是并没有加括号去执行
    return view  # 返回view,接下来才执行。

1.请求来了,匹配成功之后,会在路由中进行匹配

url('^index/', views.Index.as_view())

# 这里的 views.Index.as_view() 是一个函数的内存地址,或者说:一定是一个可调用对象

2.此时,会传一个request对象进去

url('^index/', views.Index.as_view()(request))

3.本质就是内层函数的闭包函数view()

url('^index/', views.Index.view(request))

4.view()中,执行了self.dispatch()

url('^index/', views.Index.dispatch(request))
    def dispatch(self, request, *args, **kwargs):
		# 说白了就是判断请求方式,是不是在这个 self.http_method_names 里头,如果在handler就是一个请求模式的方法,比如get或者post
        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) # 执行并返回了

5.最后,if request.method.lower() in self.http_method_names

def dispatch(self, request, *args, **kwargs):
    # 判断请求方式,是不是在这个 self.http_method_names 里,如果在handler就是一个请求模式的方法,比如get或者post
    if request.method.lower() in self.http_method_names:    # 这里 self.http_method_names 的 self 寻找顺序需要理清楚,这里的self,是Index
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)  # 执行并返回了

这里的self就是Index

也就是说,我们可以在Index中,自己定义一个http_method_names列表,来限制允许的请求

class Index(View):
	http_method_names = ['post', 'get']	# 表示只允许post和get请求

如果没有自定义的这个列表,那么就会去找Viewhttp_method_names列表

class View(object):
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

6.总结

as_view()会执行View.dispatch()这个方法,这个方法内部其实就会执行GET或者POST方法。

FBV装饰器

FBV添加装饰器,一般我们会单独将request提取出来。

from django.shortcuts import HttpResponse

def wrapper(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner

@wrapper
def other(request):
	return HttpResponse("other...")

CBV装饰器

CBV添加装饰器的方式有三种。针对不同的应用场景可以使用不同的装饰器添加方法,但是有一点是相同的。

都需要先导入下面这个模块:

from django.utils.decorators import method_decorator

方式1 - 为单独的某个功能添加装饰器

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner

class Other(View):
    @method_decorator(wrapp)
    def get(self, request):
        return HttpResponse("get方法...")
        
     @method_decorator(wrapp)
    def post(self,request):
        return HttpResponse("post方法...")

方式2 - 为CBV类添加装饰器,可指定该装饰器作用与类下的那些方法(可添加多个)

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner


@method_decorator(wrapp,name="get")
@method_decorator(wrapp,name="post")
class Other(View):
    def get(self,request):
        return HttpResponse("get方法...")

    def post(self,request):
        return HttpResponse("post方法...")

方式3 - 为dispatch方法添加装饰器,该装饰器会作用于所有的方法。(因为入口方法不是post,就是get

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner


class Other(View):
	@method_decorator(wrapp)
    def dispatch(self, request, *args, **kwargs):
        return super(Other,self).dispatch(request, *args, **kwargs)
        
    def get(self,request):
        return HttpResponse("get方法...")

    def post(self,request):
        return HttpResponse("post方法...")

CBV实现图片上传

要求:

1.在浏览器选择要上传的文件,点击按钮上传到指定文件夹

2.文件类型只能是图片类型

3.上传的图片需要在浏览器里展示出来

1.在项目路径下创建MEDIA文件夹
2.在MEDIA文件夹下创建upload_img文件夹
3.在settings.py中配置MEDIA的路径
4.在views.py中编写上传类
5.在templates文件夹中编写upload.html模板
6.在urls.py中配置路由
# views.py
class Upload(View):
    def get(self, request):
        img_list = []
        from django.conf import settings
        for media_path in [settings.MEDIA_ROOT, ]:
            img_list.append(os.listdir(os.path.join(media_path, 'upload_img')))
        return render(request, 'upload.html', {'img_list': img_list})

    def post(self, request):
        file = request.FILES.get('myfile')
        if file:
            if str(file.name).endswith('jpg' or 'jpeg' or 'bmp' or 'gif' or 'png'):
                from django001 import settings
                path = os.path.join(settings.MEDIA_ROOT, "upload_img", file.name)
                with open(path, 'wb') as f:
                    for line in file.chunks():
                        f.write(line)
                return HttpResponse('图片上传成功')
            else:
                return HttpResponse('文件类型错误')
        else:
            return HttpResponse('请选择文件')
# upload.html

<div class="row">
        <div class="col-md-3">
            <div class="panel panel-default">
                <div class="panel-heading">图片上传</div>
                <div class="panel-body">
                    <div class="col-md-4">
                        <form action="" method="post" enctype="multipart/form-data">
                            <br><br>
                            <input type="file" name="myfile" accept="image/*"><br>
                            <button type="submit" class="btn btn-default btn-block">上传图片</button>
                            <br><br>
                        </form>
                    </div>
                </div>
            </div>
        </div>

        <div class="col-md-9">
            <div class="panel panel-default">
                <div class="panel-heading">图片列表</div>
                <div class="panel-body x-img">
                    {% for img in img_list %}
                        {% for img1 in img %}
                            <img src="/media/upload_img/{{ img1 }}">
                        {% endfor %}
                    {% empty %}
                        <h2 class="text-center">暂无照片</h2>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
# settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media").replace("\", "/")
# urls.py

urlpatterns = [
	url(r'^upload/', views.Upload.as_view()),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
原文地址:https://www.cnblogs.com/xuexianqi/p/13844870.html