Оптимизировать фильтрацию результатов Queryset в Django - PullRequest
0 голосов
/ 03 октября 2019

Я переопределяю Django Admin's list_filter (чтобы настроить фильтр, который отображается справа на пользовательском интерфейсе администратора django для просмотра списка). Следующий код работает, но не оптимизирован: он увеличивает количество запросов SQL на «количество категорий продуктов».

(Частями, на которые следует обратить внимание в следующем примере кода, являются qs.values_list('product_category', flat=True), которые возвращают толькоid (int), поэтому я должен использовать ProductCategory.objects.get(id=i).)

Хотите знать, если это можно упростить?

(Например, данные: предположим, категории продуктов "выпекаются" "жареные "сырые" и т. д., а элементы - это "хлеб", "жареная рыба", "пирог". Поэтому, когда список элементов отображается в Django Admin, все категории продуктов отображаются в столбце «Фильтровать по» с правой стороны. пользовательского интерфейса.)

from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter
from product_category.model import ProductCategory


class ProductCategoryFilter(SimpleListFilter):
    title = _('ProductCategory')
    parameter_name = 'product_category'

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)

        ordered_filter_obj_list = []
        # TODO: Works, but increases SQL queries by "number of product categories"
        for i in (
            qs.values_list("product_category", flat=True)
            .distinct()
            .order_by("product_category")
        ):
            cat = ProductCategory.objects.get(id=i)
            ordered_filter_obj_list.append((i, cat))

        return ordered_filter_obj_list

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(product_category__exact=self.value())



# P.S. Above filter is used in another class like so
class ItemAdmin(admin.ModelAdmin):
    list_filter = (ProductCategoryFilter,)

Ответы [ 3 ]

1 голос
/ 03 октября 2019

Возможно, вы ищете select_related , я не знаю вашу точную структуру моделей, но вы можете использовать ее следующим образом:

cats = set()

for p in Product.objects.all().select_related('category'):
    # Without select_related(), this would make a database query for each
    # loop iteration in order to fetch the related categories  for each product.
    cats.add(p.category)

Я предполагаю, что есть некоторая связь междуваш продукт и ProductCategory модели. Надеюсь, это поможет.

0 голосов
/ 03 октября 2019

Первый параметр в списке кортежей - это значение поиска, а второй - просто имя для отображения. Это можно сделать в одном запросе SQL или с помощью Django ORM:

def lookups(self, request, model_admin):
  qs = model_admin.get_queryset(request).select_related('product_category')
  values = qs.values('product_category_id', 'product_category__name') #assuming ProductCategory has an attribute 'name'
  unique_categories = values.distinct('product_category_id', 'product_category__name')
  categories = []
  for c in unique_categories:
    categories.append((c['product_category_id'], c['product_category__name']))
  return categories
0 голосов
/ 03 октября 2019

Ха, формулировка вопроса проясняет в вашей голове! Через несколько минут после публикации нашел ответ:

(Вместо того, чтобы делать objects.get() внутри цикла for, мы можем сделать objects.all() (который является одним SQL-запросом) и заполнить временный словарь. Затем используйте этот временный аргумент, чтобы найти соответствующее строковое значение.)

def lookups(self, request, model_admin):
    qs = model_admin.get_queryset(request)

    category_list = {}
    for x in ProductCategory.objects.all():
        category_list[x.id] = str(x)

    ordered_filter_obj_list = []
    for i in (
        qs.values_list("product_category", flat=True)
        .distinct().order_by("product_category")
    ):
        ordered_filter_obj_list.append((i, category_list[i]))

    return ordered_filter_obj_list
...