django - * _set Django模型上的属性

标签 django django-models

我对django.db.models有一个非常基本的问题。

在此official django tutorial中,如果搜索单词“choice_set”,则会看到未在任何地方声明变量“choice_set”,尽管很神奇,我们可以在代码中开始使用它。

我想知道,django.db.models.Model会神奇地创建* _set变量吗?它还会创建什么其他变量?

最佳答案

您可以使用dir函数获取类的完整列表,包括您定义的类和为其定义的类,只需执行

 dir(Poll)

您最终会看到一些类似的东西(尽管不完全一样,我正在以一种环岛的方式构建它):
['DoesNotExist', 'MultipleObjectsReturned', '__class__', '__delattr__',
'__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__',
'__init__', '__metaclass__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__',
 '__weakref__', '_base_manager', '_default_manager', '_deferred', '_get_FIELD_display', 
'_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val', 
'_get_unique_checks', '_meta', '_perform_date_checks', '_perform_unique_checks', '_set_pk_val', 
'clean', 'clean_fields', 'curve_set', 'date_error_message', 'delete', 'full_clean', 'objects', 
'pk', 'prepare_database_save', 'save', 'save_base', 'choice_set',
'serializable_value', 'unique_error_message', 'validate_unique']

有很多值(value)!我们可以看到DoesNotExistMultipleObjectsReturned等异常,以及最重要的objects。但是其中一些属性不是Django添加的。如果执行dir(object()),则会在所有对象中找到属性列表:
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

通常,您可以忽略以两个__开头和结尾的代码。其他大多数由Django添加。

至于实际设置方式和位置:Django使用models.Model元类动态设置每个新模型的大多数属性。首先要知道的是,您可以使用setattr函数将成员或方法动态添加到类中:
class X:
    pass
setattr(X, "q", 12)
print X.q  # prints 12

这样就可以仅根据属性名称创建新属性。

在本教程中,允许其开始定义这些额外属性的重要行是:
class Poll(models.Model):

这意味着Poll类继承了models.Model类(属于Django)。继承具有许多有用的属性-基本上,Poll类继承了models.Model类已设置的某些行为-但它定义大多数这些新属性的位置在Model metaclass中。元类是一个棘手的概念,但从根本上讲,它们是创建新类的秘诀,通过定义一个,Django在定义models.py元类时就可以介入,并定义任何新类。

可以在here(从第55行开始)中找到Model元类的代码-这是一组实际上实际上是从头开始逐步创建类的代码。尽管看起来很复杂,但只需查看变量名称,您就可以从中受益匪浅。例如,看一下很有名的add_to_class方法:
def add_to_class(cls, name, value):
    if hasattr(value, 'contribute_to_class'):
        value.contribute_to_class(cls, name)
    else:
        setattr(cls, name, value)

除了'contribute_to_class的一种特殊情况(对您的兴趣并不重要)之外,这是一种向类添加新属性(例如方法或成员)的方法。调用它的地方给我们一些提示:
 class.add_to_class('DoesNotExist', subclass_exception(str('DoesNotExist') ...<truncated>...

它在此处添加了DoesNotExist异常,如果您请求的Poll不存在,则返回该异常。 (通过运行Poll.objects.get(pk=1337)或直接键入Poll.DoesNotExist自己查看)。

但是Django实际上比这还要复杂。您要查询的特定_set属性并非针对每个模型都构建-当字段通过ForeignKey与另一个字段相关时就会创建该属性(就像您的PollChoice一样)。分配它的各个地方都非常复杂,但是基本上所有内容都回到了related.py中的这个get_accessor_name函数
def get_accessor_name(self):
    # This method encapsulates the logic that decides what name to give an
    # accessor descriptor that retrieves related many-to-one or
    # many-to-many objects. It uses the lower-cased object_name + "_set",
    # but this can be overridden with the "related_name" option.
    if self.field.rel.multiple:
        # If this is a symmetrical m2m relation on self, there is no reverse accessor.
        if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
            return None
        return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
    else:
        return self.field.rel.related_name or (self.opts.object_name.lower())

只是想出名字而已,以弄清楚如何将其添加到类中并不是一件容易的事。但我希望您从中看到Django有很多机会添加这样的属性。

关于django - * _set Django模型上的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14228477/

相关文章:

python - 如何在 Django ORM 中随时间存储数据?

django - __str__ 返回非字符串(类型 NoneType)

python - 使用电子邮件作为身份验证字段并在 django 自定义用户模型上添加电子邮件验证

django - 在 Django Admin 中更改模型的组

django - 在 django 中调用 create 时是否隐式调用了 save()?

python - Django 可以找到我的静态文件,Pycharm 无法解析它们

python - Django 和 Reportlab 问题

javascript - 如何使用 Javascript 动态填充 Django 模板中的选择列表?

django - ArrayField 存储自定义字段值

python - 具有自定义 API View 的 Django 休息框架分页