我有一个非常基本的问题django.db.models
。
在这个官方的 django 教程中,如果你搜索单词 " choice_set
",你会看到变量 " choice_set
" 没有在任何地方声明,虽然神奇的是,我们可以开始在代码中使用它。
我想知道,django.db.models.Model
这神奇地创建了 *_set 变量是做什么的,它还创建了哪些其他变量?
我有一个非常基本的问题django.db.models
。
在这个官方的 django 教程中,如果你搜索单词 " 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']
这是很多价值!我们可以看到像DoesNotExist
和这样的例外MultipleObjectsReturned
,以及最重要的例外,objects
。但是其中一些属性不是由 Django 添加的。如果你这样做dir(object())
,你会发现所有对象的属性列表:
['__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
——但它定义大多数这些新属性的地方是在模型元类中。元类是一个棘手的概念,但基本上它们是创建新类的秘诀,通过定义一个类,Django 可以在models.py
定义元类时介入,并定义任何新的 .
模型元类的代码可以在这里找到(从第 55 行开始)——它实际上是从头开始逐步创建类的一组代码。尽管看起来很复杂,但您只需查看变量名就可以从中受益匪浅。例如,看看有希望命名的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 实际上比这更复杂。您询问的特定 属性不是为每个模型构建的 - 它是在一个字段通过 a 与另一个字段相关时创建的(就像您的and一样)。它被分配的各个地方都非常复杂,但基本上都回到了related.py中的这个函数_set
ForeignKey
Poll
Choice
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 有很多机会添加这样的属性。
这是一些ForeignKey
魔法:)
该Choice
模型具有属性poll
,它是ForeignKey
一个Poll
对象。Djangochoice_set
为Poll
对象添加了便捷方法,这将给出一个QuerySet
包含所有Choice
引用该对象的Poll
对象。
所以,给定这个(伪)代码
myPoll = Poll(question='Is Django awesome?')
option_yes = Choice(poll=myPoll, choice='Yes')
option_no = Choice(poll=myPoll, choice='No')
你可以稍后运行
poll = Poll.objects.get(question='Is Django awesome?')
options = poll.choice_set.all()
和选项将包括相关的Choice
对象。
您可以related_name
在定义ForeignKey
.
在文档中,您指的是https://docs.djangoproject.com/en/1.2/intro/tutorial01/#intro-tutorial01
'_set' 指的是与 Poll 对象相关的所有选项,因为 Choice 模型具有 poll ,它是 ID 的 Poll 的外键。
例如:
考虑投票表行(1,'你好吗?','2014 7 3')
和选择表
当你说
p = Poll.objects.get(id=1)
//现在 p 是一个投票对象,它包含 <1,'How are you?','2014 7 3'> p.choice_set.create(choice="1st",votes=8) // 在 Choice 表中它创建一个引用投票 ID 1 的行,即选择表行 (1,1,'1st',8)
基本上 _set 指的是 Choice 表(poll)的元组,指的是 Poll 表(ID)
Choice 有一个用于 Poll 的 ForeignKey,Django 自动生成便捷方法 Poll.choice_set() 来访问所有引用该投票的选项。
有一篇很棒的文章涵盖了 Django 文档中的向后关系的主题:
https://docs.djangoproject.com/en/1.11/topics/db/queries/#following-relationships-backward