英文:
Where is `.objects` attribute is added to an instance namespace in django's models.Model class?
问题
在views.py
中,当我们编写类似于any_model_name.objects.all()
的代码时,这个操作会获取所有曾经从any_model_name
类模型中初始化的对象。我刚刚阅读了models.Model
类中的所有代码(这是任何模型都应该继承的类),但我找不到.objects
属性出现的位置,也没有类似.all()
方法的属性。我的PyCharm也持有相同的观点,它会用红色下划线标记.objects
,并显示"Unresolved attribute reference 'objects' for class 'any_model_name'"。是什么神奇的东西阻止我找到Django代码中首次出现.objects
的地方呢?
英文:
When in views.py we code something like any_model_name.objects.all()
this thing brings us all the objects ever initiated from any_model_name
class-model. I've just read all code in models.Model
class (the one any model should inherit from) and I just can't find the place where .objects
attribute appears there is nothing like that as well as .all()
method attribute. My pycharm has the same opinion, it underlines .objects
in red and says "Unresolved attribute reference 'objects' for class 'any_model_name'". What the magic prevents me from finding the place in django code where .objects
thing appears for the first time?
答案1
得分: 1
这是由元类添加的,但有点晦涩。事实上,在ModelBase
的_preprare
类中,它是Model
的typeclass,而不是Model
的超类,将其贡献给类 <sup>[源码]</sup>:
> <pre><code> def _prepare(cls):
> # …
> if not opts.managers:
> # …
> <b>cls.add_to_class("objects", manager)</b>
> # …</code></pre>
.add_to_class
方法将调用manager的.add_to_class
,然后将自身设置为类对象的属性。
然而,每个模型都有一个.objects
的说法并不正确。只有在您未指定管理器时才是这样。事实上,如果您定义一个模型:
<pre><code>class MyModel(models.Model):
<b>mymanager = models.Manager()</b></code></pre>
那么MyManager
将不包含.objects
。
至于.all()
方法本身,它与_get_queryset_methods
一起工作。这些返回一个包含方法名和方法的字典。这将返回一个将'all'
映射到返回self.get_queryset().all()
的方法的字典,因为get_queryset
将返回一个QuerySet
,因此它将调用该查询集的.all()
<sup>[源码]</sup>:
> @classmethod
> def get_queryset_methods(cls, queryset_class):
> def create_method(name, method):
> @wraps(method)
> def manager_method(self, *args, **kwargs):
> return getattr(self.get_queryset(), name)(*args, **kwargs)
>
> return manager_method
>
> new_methods = {}
> for name, method in inspect.getmembers(
> queryset_class, predicate=inspect.isfunction
> ):
> # Only copy missing methods.
> if hasattr(cls, name):
> continue
> # Only copy public methods or methods with the attribute
> # queryset_only=False.
> queryset_only = getattr(method, "queryset_only", None)
> if queryset_only or (queryset_only is None and name.startswith("")):
> continue
> # Copy the method onto the manager.
> new_methods[name] = create_method(name, method)
> return new_methods
大多数IDE、代码分析工具等实际上无法推断出模型具有.objects
属性,因为它源自类型类中的一些复杂代码。
PyCharm专业版有一个Django插件,可以更好地“理解”Django,因为开发人员已经硬编码了这样的逻辑。它包含一些逻辑,不分析类型类代码,而是简单地知道,因为模块的开发人员说如此,添加了该管理器。
Python是一种非常灵活的、动态类型的语言,这往往是个好主意。但是,与静态类型的语言相比,代码分析有时无法跟踪元类等复杂性,因此最终无法看到项目如何添加到类中,这是与静态类型的语言相比的一个不足之处。
英文:
It is added by the metaclass, but in a bit of a cryptical way. Indeed, in the _preprare
class of the ModelBase
which is not the superclass of a Model
, but its typeclass, contributes this to the class <sup>[src]</sup>:
> <pre><code> def _prepare(cls):
> # …
> if not opts.managers:
> # …
> <b>cls.add_to_class("objects", manager)</b>
> # …</code></pre>
The .add_to_class
method will call the .add_to_class
on the manager, that will then set itself as attribute on the class object.
The claim that every model has however an .objects
is not true. This is only the case if you do not specify a manager yourself. Indeed, if you define a model:
<pre><code>class MyModel(models.Model):
<b>mymanager = models.Manager()</b></code></pre>
Then MyManager
will not contain an .objects
.
As for the .all()
method itself, it works with the _get_queryset_methods
. These return a dictionary with the name of the methods and its method. This will thus return a dictionary that maps 'all'
to a method that returns self.get_queryset().all()
, since get_queryset
will return a QuerySet
, it will thus call .all()
on that queryset <sup>[src]</sup>:
> @classmethod
> def get_queryset_methods(cls, queryset_class):
> def create_method(name, method):
> @wraps(method)
> def manager_method(self, *args, **kwargs):
> return getattr(self.get_queryset(), name)(*args, **kwargs)
>
> return manager_method
>
> new_methods = {}
> for name, method in inspect.getmembers(
> queryset_class, predicate=inspect.isfunction
> ):
> # Only copy missing methods.
> if hasattr(cls, name):
> continue
> # Only copy public methods or methods with the attribute
> # queryset_only=False.
> queryset_only = getattr(method, "queryset_only", None)
> if queryset_only or (queryset_only is None and name.startswith("")):
> continue
> # Copy the method onto the manager.
> new_methods[name] = create_method(name, method)
> return new_methods
Most IDE's, code analysis tools, etc. will indeed not be capable to derive that a model has an .objects
attribute, because it originates out of some sophisticated code in the typeclass.
PyCharm professional however has a Django plugin that can "understand" Django better, because the developers have hardcoded such logic. It thus contains some logic that does not analyze the typeclass code, but simply knows, because the developers of the module say that, added the manager.
Python is very flexible, dynamically-typed language, which often is a good idea. But code analysis sometimes can not follow the complexity of metaclasses, etc. and thus eventually fail to see how items are added to a class, which is a downside, compared to statically typed languages, for example.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论