Всегда откладывайте поле в Django

Как сделать поле в модели Django отложенным для всех запросов этой модели?

Исследование

Это было запрошено как функция в 2014 году и отклонено в 2022.

Не имея такой возможности, очевидная идея состоит в том, чтобы сделать пользовательский менеджер, подобный этому:

class DeferedFieldManager(models.Manager):
    use_for_related_fields = True
    def __init__(self, defered_fields=[]):
        super().__init__()
        self.defered_fields = defered_fields

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs
            ).defer(*self.defered_fields)

class A(models.Model):
    big_field = models.TextField(null=True)
    b = models.ForeignKey(B, related_name="a_s")

    objects = DeferedFieldManager(["big_field"])

class B(models.Model):
    pass

class C(models.Model):
    a = models.ForeignKey(A)

class D(models.Model):
    a = models.OneToOneField(A)

Однако, хотя это работает для A.objects.first() (прямой поиск) и B.objects.first().a_s.all() (один к одному), это не работает для C.objects.first().a (многие к одному) или D.objects.first().a (один к одному).

Простой способ проверить это - удалить поле, которое должно быть отложено, из базы данных, и код будет выдавать ошибку OperationalError: no such column, только если поле не отложено должным образом.

Как мне сделать это поле отложенным для всех способов загрузки модели (без необходимости ставить вызов defer на каждый запрос)?

Это работает на Django 1.4.22, я использую это в админ-панели, чтобы сделать ответ из БД меньше:

C.objects.select_related('a').defer('a__some_field').first()

И это тоже должно работать (не проверено):

D.objects.select_related('a').defer('a__some_field').first()

Мы создали пользовательские методы queryset, в вашем случае:

class C_obj_queryset(QuerySet):
    
    def get_deferred_for_related_model(self, model):
        return self.select_related(model).defer(*model.objects.defered_fields)

больше здесь: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#defer

Вы можете отложить загрузку полей в связанных моделях (если связанные модели загружаются с помощью select_related()), используя стандартную нотации двойного подчеркивания для разделения связанных полей.

Установите Meta.base_manager_name на 'objects'.

class A(models.Model):
    big_field = models.TextField(null=True)
    b = models.ForeignKey(B, related_name="a_s")

    objects = DeferedFieldManager(["big_field"])

    class Meta:
        base_manager_name = 'objects'

From https://docs.djangoproject.com/en/4.1/topics/db/managers/#using-managers-for-related-object-access:

Использование менеджеров для доступа к связанным объектам

По умолчанию Django использует экземпляр класса Model._base_manager менеджера при доступе к связанным объектам (т.е. choice.question), а не _default_manager на связанном объекте. Это связано с тем, что Django должен иметь возможность получить связанный объект, даже если в противном случае он будет отфильтрован (и, следовательно, недоступен) менеджером по умолчанию.

.

Если обычный базовый класс менеджера (django.db.models.Manager) не подходит для ваших обстоятельств, вы можете указать Django, какой класс использовать, установив Meta.base_manager_name.

Вернуться на верх