Django有3种视图与说明

本文深入探讨Django中的三种视图类型:函数视图、类视图及通用视图,并重点介绍了通用视图DetailView的工作原理,包括其方法流程、类属性及关键函数的实现细节。

Django有3种视图:
函数视图 FBV
类视图 CBV
通用视图 CBGV
函数视图 FBV
通过条件分支来处理不同http请求

# 函数视图  FBV
def my_view(request):
    if request.method == 'GET':
        return HttpResponse("xxx")
    elif request.method == 'POST':
        return HttpResponse("xxx")
    elif request.method == 'DELETE':
        return HttpResponse("xxx")
1
2
3
4
5
6
7
8
# urls
from . import views
urlpatterns = [
    path('', views.my_view, name='index')
]
1
2
3
4
5
缺点:函数式编程-面向过程,代码复用率低

类视图 CBV
继承自View类,通过不同实例方法来响应http请求

# 面向对象编程
from django.views.generic import View

class MyView(View):
    def get(self,request,*args,**kwargs):
        pass
    def post(self,request,*args,**kwargs):
        pass
1
2
3
4
5
6
7
8
# urls
from . import views

# 使用as_view()方法,为基于类的视图提供一个类似函数的入口
urlpatterns = [
    path('', views.MyView.as_view(), name='index')
]
1
2
3
4
5
6
7
通用视图 CBGV
在类视图上更进一步,把增删改查操作的具体方法抽象出来,使用更少的代码实现功能

以通用显示视图 DetailView 为例:
该视图从以下视图继承方法和属性,MRO顺序:
想了解MRO的具体原理移步我的另一篇博文: https://blog.csdn.net/weixin_44955304/article/details/105175789

DetailView(自身)
django.views.generic.detail.SingleObjectTemplateResponseMixin
django.views.generic.base.TemplateResponseMixin
django.views.generic.detail.BaseDetailView
django.views.generic.detail.SingleObjectMixin
django.views.generic.base.ContextMixin
django.views.generic.base.View

方法流程图

具有(可重写-自定义)的类属性

属性    Defined in
content_type = None    TemplateResponseMixin
context_object_name = None    SingleObjectMixin
extra_context = None    ContextMixin
http_method_names = [‘get’, ‘post’, ‘put’, ‘patch’, ‘delete’, ‘head’, ‘options’, ‘trace’]    View
model = None    SingleObjectMixin
pk_url_kwarg = ‘pk’    SingleObjectMixin
query_pk_and_slug = False    SingleObjectMixin
queryset = None    SingleObjectMixin
response_class = <class ‘django.template.response.TemplateResponse’>    TemplateResponseMixin
slug_field = ‘slug’    SingleObjectMixin
slug_url_kwarg = ‘slug’    SingleObjectMixin
template_engine = None    TemplateResponseMixin
template_name = None    TemplateResponseMixin
template_name_field = None    SingleObjectTemplateResponseMixin
template_name_suffix = ‘_detail’    SingleObjectTemplateResponseMixin
按照Django官方文档所述流程看,通用类视图代码
setup

def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
    self.request = request
    self.args = args
    self.kwargs = kwargs
1
2
3
4
5
dispatch()

def dispatch(self, request, *args, **kwargs):
    # 当request 请求参数在自定义类属性self.http_method_names中
    if request.method.lower() in self.http_method_names:
        # 处理器 = 获取当前类的request.method.lower()值,默认获取值为self.http_method_not_allowed
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    # 或者请求参数不存在
    else:
        handler = self.http_method_not_allowed
    # handler = get/post/delete... 与上文类视图 处理请求类似
    return handler(request, *args, **kwargs)
1
2
3
4
5
6
7
8
9
10
http_method_not_allowed()

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 http.HttpResponseNotAllowed(self._allowed_methods())
1
2
3
4
5
6
7
8
def _allowed_methods(self):
    return [m.upper() for m in self.http_method_names if hasattr(self, m)]
1
2
get_template_names()
有两个类(SingleObjectTemplateResponseMixin/TemplateResponseMixin )定义了该函数,按照MRO算法,此处使用的是SingleObjectTemplateResponseMixin中定义的get_template_names()

ps: SingleObjectTemplateResponseMixin类继承自TemplateResponseMixin类

def get_template_names(self):
try:
    # 调用父类TemplateResponseMixin中的get_template_names()方法
    names = super().get_template_names()
except ImproperlyConfigured:
    # If template_name isn't specified, it's not a problem --
    # we just start with an empty list.
    names = []
    # 如果设置了self.template_name_字段,则获取该字段的值
    if self.object and self.template_name_field:
        name = getattr(self.object, self.template_name_field, None)
        if name:
            names.insert(0, name)
    # 仅当所讨论的对象是模型时才使用此选项
    if isinstance(self.object, models.Model):
        object_meta = self.object._meta
        names.append("%s/%s%s.html" % (
            object_meta.app_label,
            object_meta.model_name,
            self.template_name_suffix
        ))
    elif getattr(self, 'model', None) is not None and issubclass(self.model, models.Model):
        names.append("%s/%s%s.html" % (
            self.model._meta.app_label,
            self.model._meta.model_name,
            self.template_name_suffix
        ))
    # If we still haven't managed to find any template names, we should
    # re-raise the ImproperlyConfigured to alert the user.
    if not names:
        raise
return names

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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())
1
2
3
4
5
6
get_slug_field()

def get_slug_field(self):
    """Get the name of a slug field to be used to look up by slug."""
    return self.slug_field
1
2
3
get_queryset()

def get_queryset(self):
"""
Return the `QuerySet` that will be used to look up the object.
This method is called by the default implementation of get_object() and
may not be called if get_object() is overridden.
"""
# 如果self.queryset属性 没有自定义
if self.queryset is None:
    # 如果定义了 self.model 属性
    if self.model:
        # 返回模型类的所有查询集(_default_manager代表Model的默认管理器)
        return self.model._default_manager.all()
    else:
        raise ImproperlyConfigured(
            "%(cls)s is missing a QuerySet. Define "
            "%(cls)s.model, %(cls)s.queryset, or override "
            "%(cls)s.get_queryset()." % {
                'cls': self.__class__.__name__
            }
        )
# 返回自定义查询集的 所有查询结果
return self.queryset.all()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
get_object()

def get_object(self, queryset=None):
    """
    返回视图显示的对象。
    在url参数中要求 `self.queryset` and a `pk` or `slug` .
    S子类可以重写此项以返回任何对象。
    """
    # Use a custom queryset if provided; this is required for subclasses
    # like DateDetailView

    if queryset is None:
        queryset = self.get_queryset()
    # Next, try looking up by primary key.
    pk = self.kwargs.get(self.pk_url_kwarg)
    slug = self.kwargs.get(self.slug_url_kwarg)
    if pk is not None:
        queryset = queryset.filter(pk=pk)
    # Next, try looking up by slug.
    if slug is not None and (pk is None or self.query_pk_and_slug):
        slug_field = self.get_slug_field()
        queryset = queryset.filter(**{slug_field: slug})
    # If none of those are defined, it's an error.
    if pk is None and slug is None:
        raise AttributeError(
            "Generic detail view %s must be called with either an object "
            "pk or a slug in the URLconf." % self.__class__.__name__
        )
    try:
        # Get the single item from the filtered queryset
        obj = queryset.get()
    except queryset.model.DoesNotExist:
        raise Http404(_("No %(verbose_name)s found matching the query") %
                      {'verbose_name': queryset.model._meta.verbose_name})
    # 返回查询结果对象
    return obj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
get_context_object_name()

def get_context_object_name(self, obj):
    """Get the name to use for the object."""
    if self.context_object_name:
        return self.context_object_name
    elif isinstance(obj, models.Model):
        return obj._meta.model_name
    else:
        return None
1
2
3
4
5
6
7
8
get_context_data()

同样有两个类(SingleObjectMixin/ContextMixin)实现了改方法 ,按照MRO,该方法属于SingleObjectMixin

def get_context_data(self, **kwargs):
    """Insert the single object into the context dict."""
    context = {}
    if self.object:
        context['object'] = self.object
        context_object_name = self.get_context_object_name(self.object)
        if context_object_name:
            context[context_object_name] = self.object
    context.update(kwargs)
    return super().get_context_data(**context)
1
2
3
4
5
6
7
8
9
10
get()

def get(self, request, *args, **kwargs):
    self.object = self.get_object()
    context = self.get_context_data(object=self.object)
    return self.render_to_response(context)
1
2
3
4
render_to_response()

def render_to_response(self, context, **response_kwargs):
    """
    Return a response, using the `response_class` for this view, with a
    template rendered with the given context.
    Pass response_kwargs to the constructor of the response class.
    """
    response_kwargs.setdefault('content_type', self.content_type)
    # response_class = <class 'django.template.response.TemplateResponse'>
    return self.response_class(
        request=self.request,
        template=self.get_template_names(),
        context=context,
        using=self.template_engine,
        **response_kwargs
    )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TemplateResponse: 标准的HttpResponse对象是静态构造的,TemplateResponse对象则是惰性构造的,它保持自己的所有上下文、模板用以构造真正的response,但只有在最后需要的时候才真正进行渲染。Django之通用类视图DetailView_Miles_sudo的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值