Запросы
Запустим наш шелл в Django и начнем работу:
python manage.py shell_plus
Отобразим все экземпляры Employee.
>>> Employee.objects.all()
<QuerySet []>
Как и ожидалось, таблица пуста, и вызов метода возвращает пустой результат. Две вещи, на которые следует обратить внимание, это objects.all() и QuerySet. Весь ввод данных в базу данных и из нее через Django ORM происходит через интерфейс объекта. Все результаты заворачиваются в QuerySet, даже пустые.
Откуда экземпляр objects?
>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> B.mro()
[<class 'B'>, <class 'A'>, <class 'object'>]
В приведенном выше примере класс B наследует класс A. Метод mro возвращает порядок от дочернего к родительскому классу, B наследует A, A наследует object. Как видите, у класса A в коде нет унаследованного класса, но метод mro говорит, что A наследует object.
Все классы Python наследуется от объекта, следовательно, mro метод возвращается object как последний родитель в списке.
>>> Employee.mro()
[<class 'hr.models.Employee'>, <class 'django.db.models.base.Model'>, <class 'django.db.models.utils.AltersData'>, <class 'object'>]
MRO для Employee возвращает ожидаемый результат:
>>> Employee
<class 'hr.models.Employee'>
>>> Employee.objects
<django.db.models.manager.Manager object at 0x0000023CB3A1FD10>
>>> Employee.objects.mro()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Manager' object has no attribute 'mro'
>>> Employee.objects.__class__.mro()
[<class 'django.db.models.manager.Manager'>, <class 'django.db.models.manager.BaseManagerFromQuerySet'>, <class 'django.db.models.manager.BaseManager'>, <class 'object'>]
Представление Employee.objects отличается от представления классов Employee. Как видно из кода выше, objects, экземпляр класса Manager, наследует BaseManagerFromQuerySet и BaseManager.
Модель Employee становится <app_name>_employee таблицей в базе данных. first_name и last_name станут столбцами в таблице. Свойства каждого поля объявляются путем создания экземпляра соответствующего класса. Ниже приведен порядок разрешения методов для CharField.
>>> from django.db import models
>>> models.CharField.mro()
[<class 'django.db.models.fields.CharField'>, <class 'django.db.models.fields.Field'>, <class 'django.db.models.query_utils.RegisterLookupMixin'>, <class 'object'>]
CharField наследует Field и наследует RegisterLookUpMixin.
Роль класса Field
- Роль класса
fieldзаключается в сопоставлении типа поля с типом базы данных SQL. - Сериализация - преобразование объекта Python в соответствующее значение базы данных SQL.
- Десериализация - преобразование значения базы данных SQL в объект Python.
- Валидации на уровне поля и встроенные проверки перед сериализацией данных.
Например, в полеPositiveIntegerFieldзначение должно быть больше нуля - встроенное ограничение.
Структура Field класса
>>> models.Field.__subclasses__()
[django.db.models.fields.BooleanField,
django.db.models.fields.CharField,
django.db.models.fields.DateField,
django.db.models.fields.DecimalField,
django.db.models.fields.DurationField,
django.db.models.fields.FilePathField,
django.db.models.fields.FloatField,
django.db.models.fields.IntegerField,
django.db.models.fields.IPAddressField,
django.db.models.fields.GenericIPAddressField,
django.db.models.fields.TextField,
django.db.models.fields.TimeField,
django.db.models.fields.BinaryField,
django.db.models.fields.UUIDField,
django.db.models.fields.json.JSONField,
django.db.models.fields.files.FileField,
django.db.models.fields.related.RelatedField,
django.contrib.postgres.search.SearchVectorField,
django.contrib.postgres.search.SearchQueryField,
fernet_fields.fields.EncryptedField,
enumchoicefield.fields.EnumChoiceField,
django.contrib.postgres.fields.array.ArrayField,
django.contrib.postgres.fields.hstore.HStoreField,
django.contrib.postgres.fields.ranges.RangeField]
Это поля "высокого уровня". Например, Django реализует другие поля, которые наследуют вышеуказанные поля.
Например, EmailField наследует CharField.
>>> models.CharField.__subclasses__()
[django.db.models.fields.CommaSeparatedIntegerField,
django.db.models.fields.EmailField,
django.db.models.fields.SlugField,
django.db.models.fields.URLField,
django.contrib.postgres.fields.citext.CICharField,
django_extensions.db.fields.RandomCharField,
django_extensions.db.fields.ShortUUIDField]
Если мы посмотрим код class Field(RegisterLookupMixin), то увидим инициализацию следующих переменных:
def __init__(
self,
verbose_name=None,
name=None,
primary_key=False,
max_length=None,
unique=False,
blank=False,
null=False,
db_index=False,
rel=None,
default=NOT_PROVIDED,
editable=True,
serialize=True,
unique_for_date=None,
unique_for_month=None,
unique_for_year=None,
choices=None,
help_text="",
db_column=None,
db_tablespace=None,
auto_created=False,
validators=(),
error_messages=None,
db_comment=None,
):
Инициализатор Field содержит 23 аргумента. Большинство аргументов относятся к свойствам столбца базы данных SQL, а остальные аргументы относятся к формам администратора и модели Django.
Параметры, поддерживаемые полями всех типов
verbose_name- "человеческое" название поля, которое будет выводиться на веб-страницах. Если не указано, то будет выводиться имя поля;
help_text- дополнительный поясняющий текст, выводимый на экран. По умолчанию - пустая строка. Содержащиеся в этом тексте специальные символы HTML не преобразуются в литералы и выводятся как есть. Это позволяет отформатировать поясняющий текст НТМL-тегами;
default- значение по умолчанию, записываемое в поле, если в него явно не было занесено никакого значения. Может быть указано двумя способами:Как обычное значение любого неизменяемого типа:
is_active = models.BooleanField(default=True)Если в качестве значения по умолчанию должно выступать значение изменяемого типа (список или словарь Python), то для его указания следует использовать второй способ.
Как ссылка на функцию, вызываемую при создании каждой новой записи и возвращающую в качестве результата заносимое в поле значение:
def is_active_default(): return not is_all_posts_passive ... ... is_active = models.BooleanField(default=is_active_default)
unique- еслиTrue, то в текущее поле может быть занесено только уникальное в пределах таблицы значение (уникальное поле). При попытке занести значение, уже имеющееся в том же поле другой записи, будет возбуждено исключениеIntegrityErrorиз модуляdjango.db. Если поле помечено как уникальное, по нему автоматически будет создан индекс. Поэтому явно задавать для него индекс не нужно. ЕслиFalse, то текущее поле может хранить любые значения. Значение по умолчанию -False;
unique_for_date- если в этом параметре указать представленное в виде строки имя поля даты (DateField) или даты и времени (DateTimeField), то текущее поле может хранить только значения, уникальные в пределах даты, которая хранится в указанном поле. Пример:title = models.CharField(max_length=50, unique_for_date='published') published = models.DateTimeField()В этом случае Django позволит сохранить в поле
titleтолько значения, уникальные в пределах даты, хранящейся в полеpublished;unique_for_month- то же самое, что иunique_for_date, но в расчет принимается не всё значение даты, а лишь месяц;
unique_for_year- то же самое, что иunique_for_date, но в расчет принимается не всё значение даты, а лишь год;
null- еслиTrue, то поле в таблице базы данных может хранить значениеnullи, таким образом, являться необязательным к заполнению. ЕслиFalse, то поле в таблице должно иметь какое-либо значение, хотя бы пустую строку. Значение по умолчанию -False.
Отметим, что у строковых и текстовых полей, даже обязательных к заполнению (т.е. при их объявлении параметруnullбыло присвоено значениеFalse), вполне допустимое значение - пустая строка.
Если сделать поля необязательными к заполнению, задавTrueдля параметраnull, то они вдобавок к этому могут хранить значениеnull. Оба значения фактически представляют отсутствие каких-либо данных в поле, и эту ситуацию придется как-то обрабатывать.
Поэтому, чтобы упростить обработку отсутствия значения в таком поле, его не стоит делать необязательным к заполнению. Параметрnullзатрагивает только поле таблицы, но не поведение Django. Даже если какое-то поле присвоением параметру значенияTrueбыло помечено как необязательное, фреймворк по умолчанию все равно не позволит занести в него пустое значение;
blank- еслиTrue, то Django позволит занести в поле пустое значение, тем самым сделав поле необязательным к заполнению, еслиFalse- не позволит. По умолчанию -False. Параметрblankзадает поведение самого фреймворка при выводе на экран веб-форм и проверке введенных в них данных. Если этому параметру дано значениеTrue, то Django позволит занести в поле пустое значение (например, для строкового поля - пустую строку), даже если это поле было помечено как обязательное к заполнению (параметруnullбыло дано значениеFalse);
db_index- еслиTrue, то по текущему полю в таблице будет создан индекс, еслиFalse- не будет. По умолчанию -False; Данный параметр мы рассмотрим отдельно, далее по курсу.
primary_key- еслиTrue, то текущее поле станет ключевым. Такое поле будет помечено как обязательное к заполнению и уникальное (параметруnullнеявно будет присвоено значениеFalse, а параметруunique-True), и по нему будет создан ключевой индекс. В модели может присутствовать только одно ключевое поле! ЕслиFalse, то поле не будет преобразовано в ключевое. Значение по умолчанию -Fаlsе. Если ключевое поле в модели не было задано явно, сам фреймворк создаст в ней целочисленное автоинкрементное ключевое поле с именемid;
editаblе- еслиTrue, то поле будет выводиться на экран в составе формы, еслиFalse- не будет (даже если явно создать его в форме) . По умолчанию -True;
db_column- имя поля таблицы в виде строки. Если не указано, то поле таблицы получит такое же имя, что и поле модели.
Есть много и других вариантов - которые вы можете просмотреть в документации. Некоторые параметры мы изучим позже.