Ответ 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 есть много шансов добавить такие атрибуты.