背景

  最近在看django官方文档的class-based-views这一节的时候一直不得要领,感觉自己清楚,但是回想起来又没有脉络;于是没有办法只

  能是“暗中观察”django的源码了。 刚打开源码看了没有多久就疑窦丛生,比如说下面这一段,能看的出get_object方法中用到的self.kwargs

  属性是在哪里设置过呢?如果没有设置直接用是会有报错的,详细内容看下面源码

class SingleObjectMixin(ContextMixin):
"""
Provide the ability to retrieve a single object for further manipulation.
"""
model = None
queryset = None
slug_field = 'slug'
context_object_name = None
slug_url_kwarg = 'slug'
pk_url_kwarg = 'pk'
query_pk_and_slug = False def get_object(self, queryset=None):
"""
Return the object the view is displaying. Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
Subclasses can override this to return any object.
"""
# 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 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.
"""
if self.queryset is None:
if self.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() def get_slug_field(self):
"""Get the name of a slug field to be used to look up by slug."""
return self.slug_field 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 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)

  

众里寻它

  一看SingleObjectMixin类的定义发现它里面并没有kwargs这个属性、所以这个kwargs属性应该在是定义在它的父类里、ContextMixin源码如下

class ContextMixin:
"""
A default context mixin that passes the keyword arguments received by
get_context_data() as the template context.
"""
extra_context = None def get_context_data(self, **kwargs):
kwargs.setdefault('view', self)
if self.extra_context is not None:
kwargs.update(self.extra_context)
return kwargs

  !我的天kwargs没有在父类里、也不可能在object类里面。粗看起来get_object方法用使用self.kwargs已经违反Python这门语言的基本法了,但是

  这个的可能性也不太;django这么有名的一个项目,而且自己在使用这上块的功能时并没有出问题。

刻苦学习在View.as_view方法中发现新的天地

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) @classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
#不允许给View的实例传入get/post/head这样的字段、原因是这些名字是留给方法用的!
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__))
#如下View类中已经定义了这个“名字”,为了防止实例覆盖类的同名属性所以这里也不支持传入同名属性
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))
#如果上面的检查都过了,但是django并没有步入通常的套路。它在这里定义了一个叫view的函数,它在view中创建View类的实例
def view(request, *args, **kwargs):
#通过as_veiw中传入的参数来创建一个view实例、由__init__可以知道它是对**kwargs全盘接收的
#initkwargs是通过闭包的形式记录下来的
self = cls(**initkwargs)
#在没有指定`head`方法的情况下就直接用`get`来处理`head`
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
#下面这三行搞的对于任何一个通过上述方试创建出来的View实例都有了request、args、kwargs这三个属性
self.request = request
self.args = args
self.kwargs = kwargs
#调用 调度方法、以调度方法的返回结果为view这个Function的执行结果、由于调度方法会调用get | post | head ... 而这些方法都返回
#HttpResponse所以view最终返回的就是HttpResponse的实例
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 dispatch(self, request, *args, **kwargs):
"""根据http的不同请求方式、调用不同的方法、并返回HttpResponse"""
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())

  可以看到as_view里面做了一个操作叫 self.kwargs = kwargs 、看到了希望!我的三观还能保全、还要进一步求证这两个self.kwargs是不是同一个

找到关键

  1、在使用View时只能用as_view方式

urlpatterns = [
path('publishers/', PublisherList.as_view()),
]

  2、我们并不直接使用SingleObjectMixin而是使用DetailView、而DetailView就给实例加上了self.kwargs = kwargs这一层逻辑

class BaseDetailView(SingleObjectMixin, View):
"""A base view for displaying a single object."""
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) class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
template_name_field = None
template_name_suffix = '_detail' class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
"""
Render a "detail" view of an object. By default this is a model instance looked up from `self.queryset`, but the
view will support display of *any* object by overriding `self.get_object()`.
"""

  可以看到BaseDetailView把SingleObjectMixin和View、也就是说第一个BaseDetailView的实例都会有kwargs属性,这个原因可以追溯到

  “刻苦学习在View.as_view方法中发现新的天地”这一节中提到的self.kwargs = kwargs。

  由于View的实例都是由as_view创建的、在as_view的逻辑里面就会给实例加上kwargs属性、SingleObjectMixin.get_object中执行self.kwargs

  自然就不会有错误了!

DetailView相关的层次结构

----

django --- DetailView源码分析的更多相关文章

  1. $Django cbv源码分析 djangorestframework框架之APIView源码分析

    1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...

  2. Django——admin源码分析

    在Django中,如果我们新建一个项目,只要在admin.py文件中注册,就可以对其相应的文件进行增删改查操作. 而我们在路由系统中只看到了一条信息:url(r'^admin/', admin.sit ...

  3. Django——Session源码分析

    首先我们导入django.contrib.sessions.middleware这个中间件,查看里面的Session源码 from django.contrib.sessions.middleware ...

  4. Django之REST framework源码分析

    前言: Django REST framework,是1个基于Django搭建 REST风格API的框架: 1.什么是API呢? API就是访问即可获取数据的url地址,下面是一个最简单的 Djang ...

  5. django Rest Framework----APIView 执行流程 APIView 源码分析

    在django—CBV源码分析中,我们是分析的from django.views import View下的执行流程,这篇博客我们介绍django Rest Framework下的APIView的源码 ...

  6. django---APIView源码分析

    django---APIView源码分析 前言:APIView基于View 看这部分内容一定要懂django-CBV里的内容 在django-CBV源码分析中,我们是分析的from django.vi ...

  7. admin源码分析

    django settings 源码分析 导入settingso模块,进入源码,会发现settings是一个 单例 LazySettings类实例化产生的一个对象,LazySettings实例化后就会 ...

  8. django 之(二) --- 源码分析

    CBV类视图继承 CBV:继承自View:注册的时候使用的as_view() 入口 不能使用请求方法的名字作为参数的名字 只能接受已经存在的属性对应的参数 定义了一个view 创建了一个类视图对象 保 ...

  9. Django搭建及源码分析(三)---+uWSGI+nginx

    每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...

随机推荐

  1. 【Java】 剑指offer(47) 礼物的最大价值

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值( ...

  2. Python 枚举【一】

    1. 枚举的定义 首先,定义枚举要导入enum模块. 枚举定义用class关键字,继承Enum类. 用于定义枚举的class和定义类的class是有区别[下一篇博文继续分享]. 示例代码: from ...

  3. Web轻量级扫描工具Skipfish

    Web轻量级扫描工具Skipfish 1. Skipfish 简介 2. Skipfish 基本操作 3.身份认证 一. Skipfish 简介 Skipfish是一款主动的Web应用程序安全侦察工具 ...

  4. 断言(assert)

    断言(assert):用来调试.测试代码. 格式: assert 布尔表达式: 字符串 (如果布尔表达式为false时,这个字符串才会显示). 注意: assert默认是关闭的,使用时需要使用&quo ...

  5. win7 资源资源管理器访问FTP站点

    Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\ftp] @="URL:File Transfer Protocol" ...

  6. 获取运行端口监听的用户身份auth-owner

    获取运行端口监听的用户身份auth-owner   Windows系统提供工作在TCP 113端口的授权服务(Authentication Service),用来判断TCP连接的用户.Nmap的aut ...

  7. POJ.3710.Christmas Game(博弈论 树上删边游戏 Multi-SG)

    题目链接 \(Description\) 给定n棵"树",每棵"树"的节点可能"挂着"一个环,保证没有环相交,且与树只有一个公共点. 两人轮 ...

  8. Codeforces757E.Bash Plays With Functions(积性函数 DP)

    题目链接 \(Description\) q次询问,每次给定r,n,求\(F_r(n)\). \[ f_0(n)=\sum_{u\times v=n}[(u,v)=1]\\ f_{r+1}(n)=\s ...

  9. Python数值计算之插值曲线拟合-01

        3 插值与曲线拟合 Interpolation and Curve Fitting 给定n+1个数据点(xi,yi), i = 0,1,2,…,n,评估y(x). 3.1 介绍(introdu ...

  10. 用Canvas做视频拼图

    声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢! 几天前同事给我看了一个特效,是一个拼图游戏,不同的是,拼图里的是动画.他让我看下做个DEMO,于是就自己整了一会,也确实不难.用canva ...