我有一个关于django.db.models
的基本问题。
在此official django tutorial中,如果您搜索单词“choice_set
”,您会看到变量“choice_set
”未在任何地方声明,但神奇地说,我们可以开始在代码。
我想知道,django.db.models.Model
做了什么,神奇地创建了* _set变量,以及它创建了哪些其他变量?
答案 0 :(得分:29)
您可以使用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__']
大多数情况下,您可以忽略以两个__
开头和结尾的那些。其他大部分都是由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
类已设置的一些行为 - 但它定义大多数这些新属性的位置在模型{{3}中}。元类是一个棘手的概念,但基本上它们可以作为创建新类的一个方法,通过定义一个,Django可以在定义models.py
元类时直接进入,并定义任何新类。
可以找到Model元类的代码metaclass(从第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比这复杂得多。您询问的特定 _set
属性不是为每个模型构建的 - 当一个字段与另一个字段相关时,ForeignKey
就会创建它(就像您的{{ 1}}和Poll
)。它被分配的各个地方非常复杂,但它基本上都回到here
Choice
函数
get_accessor_name
这只是提出了这个名字 - 追溯它以确定如何将它添加到课堂中并不是一件容易的事。但我希望你从中看到Django有很多机会添加这样的属性。
答案 1 :(得分:11)
这是一些ForeignKey
魔术:)
Choice
模型的属性为poll
,ForeignKey
为Poll
个对象。 Django将方便方法choice_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
选项更改此属性的名称。
答案 2 :(得分:1)
Choice有一个ForeignKey to Poll,Django自动生成便利方法Poll.choice_set()来访问引用该轮询的所有选项。
在Django文档中有一篇很好的文章介绍了以下关系的主题:
https://docs.djangoproject.com/en/1.11/topics/db/queries/#following-relationships-backward
答案 3 :(得分:0)
在您所指的文件中https://docs.djangoproject.com/en/1.2/intro/tutorial01/#intro-tutorial01
&#39; _set&#39;指的是与Poll对象相关的所有选项,因为Choice模型具有轮询,这是轮询ID的外键。
例如:
考虑民意调查表行(1,&#39;你好吗?&#39;,&#39; 2014 7 3&#39;)
和选择表
当你说
时p = Poll.objects.get(id=1)
//现在p是一个保留&lt; 1的民意调查对象,&#39;你好吗?&#39;,&#39; 2014 7 3&#39;&gt; p.choice_set.create(选择=&#34;第一&#34;,票= 8) //在Choice表中,它创建一个引用轮询ID 1的行,即选择表行(1,1,&#39; 1st&#39;,8)
基本上_set是指引用轮询表(ID)的Choice表(轮询)的元组