Создание кастомных полей модели
Джанго поставляется с полным набором полей модели, которые можно использовать для построения моделей. Однако можно также создать собственные поля модели для хранения пользовательских данных или изменения поведения существующих полей.
Нам нужно поле, позволяющее задать определенный порядок для объектов. Если вы думаете о легком способе сделать это с полем, предоставленным Джанго, вы, вероятно, подумаете о добавлении PositiveIntegerField к моделям. Это хорошая отправная точка. Можно создать настраиваемое поле, наследуемое от PositiveIntegerField предоставляющее дополнительные свойства.
Существуют две соответствующие функции, которые мы будем строить в своем поле:
- Автоматически присваивать значение order, если не предоставлен конкретный order. Если при хранении объекта order не предоставляется, наше поле должно автоматически назначать следующий order на основе последнего существующего объекта order. Если имеется два объекта с order 1 и 2, соответственно, при сохранении третьего объекта, следует автоматически назначить ему order 3, если не указан какой-либо конкретный order.
- Модули курсов будут упорядочены в отношении курса, к которому они принадлежат, и содержимого модуля по отношению к модулю, к которому они принадлежат.
Создайте новый файл fields.py в каталоге приложения courses и добавьте в него следующий код:
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
class OrderField(models.PositiveIntegerField):
def __init__(self, for_fields=None, *args, **kwargs):
self.for_fields = for_fields
super(OrderField, self).__init__(*args, **kwargs)
def pre_save(self, model_instance, add):
if getattr(model_instance, self.attname) is None:
# no current value
try:
qs = self.model.objects.all()
if self.for_fields:
# filter by objects with the same field values
# for the fields in "for_fields"
query = {field: getattr(model_instance, field) for field in self.for_fields}
qs = qs.filter(**query)
# get the order of the last item
last_item = qs.latest(self.attname)
value = last_item.order + 1
except ObjectDoesNotExist:
value = 0
setattr(model_instance, self.attname, value)
return value
else:
return super(OrderField, self).pre_save(model_instance, add)
Это наш кастомный OrderField. Он наследуется от поля PositiveIntegerField, предоставленного Джанго. Наше поле OrderField принимает необязательный параметр for_fields, который позволяет указывать поля, по которым должен быть рассчитан заказ.
Наше поле переопределяет метод pre_save() поля PositiveIntegerField, который выполняется перед сохранением поля в базе данных. В этом методе выполняются следующие действия:
- Мы проверяем, уже существует ли значение для этого поля в экземпляре модели. Мы используем self.attname, то есть имя атрибута, присвоенное полю в модели. Если значение атрибута отличается от значения None, мы вычислим порядок, который мы должны дать ему следующим образом: