在Django的models.Model类中,`.objects`属性是如何添加到实例命名空间中的?

huangapple go评论46阅读模式
英文:

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类中,它是Modeltypeclass,而不是Model的超类,将其贡献给类 &nbsp; <sup>[源码]</sup>

> <pre><code> def _prepare(cls):
> # &hellip;
> if not opts.managers:
> # &hellip;
> <b>cls.add_to_class(&quot;objects&quot;, manager)</b>
> # &hellip;</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一起工作。这些返回一个包含方法名和方法的字典。这将返回一个将&#39;all&#39;映射到返回self.get_queryset().all()的方法的字典,因为get_queryset将返回一个QuerySet因此它将调用该查询集的.all()&nbsp;<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&nbsp;<sup>[src]</sup>:

> <pre><code> def _prepare(cls):
> # &hellip;
> if not opts.managers:
> # &hellip;
> <b>cls.add_to_class(&quot;objects&quot;, manager)</b>
> # &hellip;</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 &#39;all&#39; to a method that returns self.get_queryset().all(), since get_queryset will return a QuerySet, it will thus call .all() on that queryset&nbsp;<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.

huangapple
  • 本文由 发表于 2023年4月17日 02:57:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76029775.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定