Django ORM — Аннотировать запрос другим подзапросом, сохраняя при этом результаты подзапроса

#mysql #django #orm #prefetch #annotate

Вопрос:

 #  My Database Models
ModelA(models.Model):
    referancer = models.CharField()
    ...
        
ModelXYZ(models.Model):
    name = models.CharField()
    ...
 

У меня есть такие модели, как top, и между ними нет связи с внешним ключом, также структура должна оставаться такой, потому что в моем реальном запросе фильтры и аннотации сложнее, чем в этом. Также это самый простой способ воспроизвести проблему.

 modelXYZ_subquery = ModelXYZ.objects.filter(name__exact=OuterRef('referancer')).order_by('-id')
modelA_dataset = ModelA.objects.filter(**my_custom_filters).annotate(
    has_XYZ=Exists(modelXYZ_subquery),
    latest_xyz=Subquery(modelXYZ_subquery.values('id')[:1], output_field=IntegerField)
)

for _modelA in modelA_dataset:
    my_custom_function_1(_modelA)
    if _modelA.has_XYZ:
        detected_xyz = ModelXYZ.objects.get(id=_modelA.latest_xyz) # Database Hit
    else:
        detected_xyz = create_xyz(_modelA)
    my_custom_function_2(_modelA, detected_xyz)
 

Это функция, которую я получил, но она работает слишком долго из-за огромного набора данных.

Я хочу избавиться от ненужного попадания в базу данных, которое я получил сверху в цикле for.

Есть ли в любом случае способ достичь этой цели с помощью аннотации или prefetch_related или чего-то еще?

Комментарии:

1. Почему же referancer тогда это поле не является ForeignKey а ?

2. Это не мой выбор, и структура базы данных должна оставаться такой. Просто желая ускорить работу, я спустился ниже.

Ответ №1:

Вы можете рассмотреть возможность использования JoinField. Это не изменит структуру вашей базы данных, но позволит ORM выполнять объединения так же, как и с внешними ключами. pip install django-joinfield и поместите joinfield свои установленные приложения в settings.py

 from joinfield.joinfield import JoinField

class ModelXYZ(models.Model):
    name = JoinField(ModelA, to_field='referancer', on_delete=models.DO_NOTHING)
 

миграции создадут миграцию, но миграция не изменит структуру вашей базы данных. Будет разрешено иметь значения в ModelXYZ.name которых нет в ModelA.referancer.

Теперь вы можете prefetch_related перейти от модели к модели Xyz.

Комментарии:

1. Я буду ждать последнего дня щедрости. Если лучшего ответа нет, я отредактирую свой вопрос и одобрю его в качестве ответа, сэр.