Предварительная выборка сложного запроса, выполняющего объединение
Я пытаюсь предварительно получить связанный объект, чтобы оптимизировать производительность. Код, который я пытаюсь префетчить, выглядит следующим образом;
class Product(models.Model):
    ...
    def get_attribute_values(self):
        # ToDo: Implement this prefetch.
        if hasattr(self, "_prefetched_attribute_values"):
            return self._prefetched_attribute_values
        if not self.pk:
            return self.attribute_values.model.objects.none()
        attribute_values = self.attribute_values.all()
        if self.is_child:
            parent_attribute_values = self.parent.attribute_values.exclude(
                attribute__code__in=attribute_values.values("attribute__code")
            )
            return attribute_values | parent_attribute_values
        return attribute_values
Что это делает следующим образом;
-  Получите все attribute_valuesсамого себя,attribute_values- это связанная модельProductAttributeValue
-  Если это дочерняя модель, также получите родительскую attribute_values, но исключитеattribute_valuesсattribute__codes, которые уже присутствуют в дочерней моделиattribute_valuesРезультат
- Выполните объединение, которое объединит их вместе.
В настоящее время у меня есть такой префетч;
Prefetch(
    "attribute_values",
    queryset=ProductAttributeValueModel.objects.select_related(
        "attribute", "value_option"
    )
),
Это работает для недетского сценария, но, к сожалению, дочерних продуктов много, и поэтому производительность не так велика.
Так что в идеале я могу префетчить объединенные атрибуты в '_prefetched_attribute_values', хотя я был бы не против сделать и два префетча;
- Для самого продукта
- Для родителя, но при этом необходимо исключить атрибуты, которые были у самого ребенка
Я пробовал сделать это с помощью Subquery & OuterRef, но пока безуспешно.
В итоге я пришел к следующему решению, которое, похоже, работает.
        # The base queryset for both self and parent attribute values.
        prefetch_queryset = (
            ProductAttributeValue.objects.all()
            .select_related("attribute", "value_option", "value_option__group")
            .prefetch_related(
                Prefetch(
                    "value_multi_option",
                    queryset=AttributeOption.objects.select_related("group"),
                )
            )
            .annotate(code=F("attribute__code"))
        )
        # Subquery to get the child's attribute codes
        child_attribute_codes = ProductAttributeValue.objects.filter(
            product=OuterRef("product__children")
        ).values("attribute__code")
        parent_prefetch_queryset = prefetch_queryset.exclude(
            Exists(
                child_attribute_codes.filter(
                    attribute__code=OuterRef("attribute__code")
                )
            )
        )
        # pylint: disable=not-callable
        queryset = self.select_related("parent").prefetch_related(
            Prefetch(
                "attribute_values",
                queryset=prefetch_queryset,
                to_attr="_prefetched_attribute_values",
            ),
            Prefetch(
                "parent__attribute_values",
                queryset=parent_prefetch_queryset,
                to_attr="_prefetched_parent_attribute_values",
            ),
        )