Django - Аннотирование, фильтрация и сортировка по отношениям "один ко многим" не работает
Я работаю над таблицей, которая отображает список заказов с возможностью фильтрации и сортировки в зависимости от прямого атрибута заказа или косвенно от атрибута связанных моделей. Две модели, о которых идет речь, это Order и Block. Заказ может иметь не слишком много блоков, связанных с ним, а блок всегда имеет один уникальный заказ.
class Order(CustomBaseModel):
date_of_order = models.DateField(default=timezone.now, verbose_name="Date of order"
...
class Block(CustomBaseModel):
...
order = models.ForeignKey(Order, on_delete=models.CASCADE)
...
Чтобы иметь возможность фильтровать ордера с блоком или без блока, я аннотирую свой набор запросов следующим образом:
order_queryset = Order.objects.all().annotate(
is_material_available=Case(
When(block__isnull=False, then=Value(True)),
default=Value(False),
output_field=BooleanField()
),
)
и затем используйте опцию фильтрации для новой аннотации:
is_material_available = self.data["is_material_available"]
if is_material_available == "True":
order_queryset = order_queryset.filter(is_material_available=True)
elif is_material_available == "False":
order_queryset = order_queryset.filter(is_material_available=False)
Использование этого метода приводит к такому поведению:
- is_material_available=="True": Выбирает только те заказы, у которых есть заказы, что замечательно, но полностью нарушает пагинацию. Допустим, количество заказов на странице равно 8, это создаст страницу только с одним заказом, или даже больше. Также некоторые заказы присутствуют на разных страницах.
- is_material_available=="False": Получение заказов с блоками и без блоков, связанных с ним, но пагинация работает нормально.
Я попытался изменить фильтрацию, используя:
order_queryset = order_queryset.filter(Exists(Block.objects.filter(order=OuterRef("pk")))
или
order_queryset = order_queryset.filter(block__isnull=False)
или используя этот вариант:
order_queryset = Order.objects.all().annotate(
is_material_available=Count('block', distinct=True)
)
order_queryset = order_queryset.filter(is_material_available__gt=0)
но результат тот же. Сортировка с использованием аннотации "is_material_available" не работает и сортирует заказы случайным образом, хотя SQL-запрос выглядит нормально.
Кто-нибудь знает, что происходит?
Я выяснил, что было не так. Это не имеет никакого отношения к фреймворку queryset в django, а к тому, как ордера и блоки обновлялись из двух разных баз данных. Различные методы фильтрации работают нормально.