Подтвердить что ты не робот

* _set атрибуты на моделях Django

У меня есть очень простой вопрос о django.db.models.

В этом официальном учебнике django, если вы ищете слово "choice_set", вы увидите, что переменная "choice_set" не сообщается нигде, хотя волшебным образом, мы можем начать использовать его в коде.

Интересно, что делает django.db.models.Model то, что волшебным образом создало переменную * _set и какие другие переменные она создает?

4b9b3361

Ответ 1

Вы можете получить полный список атрибутов класса, как указанных вами, так и тех, которые определены для него, с помощью функции 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, но место, которое оно определяет для большинства этих новых атрибутов, находится в Model metaclass. Метаклассы представляют собой сложную концепцию, но в основном они служат рецептом для создания новых классов, и, определяя один, Django переходит вправо, когда метаклас models.py определяется и определяет любые новые.

Код метакласса Model можно найти здесь (начиная с строки 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 (как и ваши Poll и Choice). Различные места, где он назначается, очень сложны, но в основном все возвращается к этой функции get_accessor_name в 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 есть много шансов добавить такие атрибуты.

Ответ 2

Это магия ForeignKey:)

Модель Choice имеет атрибут poll, который является объектом ForeignKey to poll. Django добавляет метод удобства choice_set к poll объектам, который даст QuerySet содержащий все Choice объекты, которые ссылаются на poll Object.

Итак, учитывая этот (псевдо) код

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.

Ответ 3

В документе вы ссылаетесь на https://docs.djangoproject.com/en/1.2/intro/tutorial01/#intro-tutorial01

'_ set' относится ко всем выборам, относящимся к объекту Poll, так как модель Choice имеет опрос, который является внешним ключом к опросу идентификатора.

Пример:

Рассмотрим строку таблицы опроса (1, "Как вы?", "2014 7 3" )

и таблица выбора

когда вы скажете

p = Poll.objects.get(id=1)

//теперь p - объект опроса, который содержит < 1, "Как вы?", "2014 7 3" >   p.choice_set.create(выбор = "первый", голоса = 8) // в таблице выбора создает строку, которая ссылается на идентификатор опроса 1 i.e. Таблица строк выбора (1,1, '1st', 8)

В основном _set относится к кортежам таблицы выбора (опроса), ссылающейся на таблицу опроса (ID)

Ответ 4

Выбор имеет ForeignKey для опроса, а Django автоматически создает метод Poll.choice_set() для доступа ко всем выборам, относящимся к этому опросу.

Там отличная статья, посвященная теме следующих отношений в документах Django:

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