您可以使用以下命令获取类的属性的完整列表,包括您定义的属性和为其定义的属性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']
这是很多价值!我们可以看到类似的异常DoesNotExist
and MultipleObjectsReturned
,以及最重要的一个,objects
。但其中一些属性并不是 Django 添加的。如果你这样做dir(object())
你会发现属性列表all对象:
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
大多数情况下,您可以忽略以 2 开头和结尾的内容__
。其他大部分是由 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
类已设置 - 但它定义大部分新属性的位置是在模型中元类 https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python。元类是一个棘手的概念,但基本上它们是创建新类的方法,通过定义元类,Django 可以在需要时介入。models.py
正在定义元类,并定义任何新的 .
可以找到模型元类的代码here https://github.com/django/django/blob/master/django/db/models/base.py(从第 55 行开始)——这是一组代码,实际上是从头开始逐步创建一个类。尽管它看起来很复杂,但只需查看变量名称就可以从中了解很多内容。例如,看看有希望命名的add_to_class
method:
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 实际上比这更复杂。这specific _set
您所询问的属性并不是为每个模型构建的 - 它是在一个字段通过一个字段与另一个字段相关时创建的ForeignKey
(就像你的Poll
and Choice
)。各个地方赋值的地方很复杂,但基本上都回到了这个get_accessor_name
函数于相关.py https://github.com/django/django/blob/master/django/db/models/fields/related.py
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 有很多机会添加这样的属性。