19

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

在这个官方的 django 教程中,如果你搜索单词 " choice_set",你会看到变量 " choice_set" 没有在任何地方声明,虽然神奇的是,我们可以开始在代码中使用它。

我想知道,django.db.models.Model这神奇地创建了 *_set 变量是做什么的,它还创建了哪些其他变量?

4

4 回答 4

31

您可以使用该函数获取类属性的完整列表,包括您定义的和为其定义的属性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中的这个函数_setForeignKeyPollChoiceget_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 有很多机会添加这样的属性。

于 2013-01-09T05:31:01.500 回答
11

这是一些ForeignKey魔法:)

Choice模型具有属性poll,它是ForeignKey一个Poll对象。Djangochoice_setPoll对象添加了便捷方法,这将给出一个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.

于 2013-01-09T05:29:06.707 回答
1

在文档中,您指的是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)

于 2014-07-02T09:26:41.063 回答
1

Choice 有一个用于 Poll 的 ForeignKey,Django 自动生成便捷方法 Poll.choice_set() 来访问所有引用该投票的选项。

有一篇很棒的文章涵盖了 Django 文档中的向后关系的主题:

https://docs.djangoproject.com/en/1.11/topics/db/queries/#following-relationships-backward

于 2017-04-27T09:55:32.710 回答