Django: нумерация страниц с prefetch_related - PullRequest
1 голос
/ 20 апреля 2020

У меня есть технические характеристики моделей, которые разделены на категории. Для шаблона, чтобы отобразить категорию в качестве заголовка в таблице, я делаю prefetch_related для категории следующим образом:

categories = Category.objects.distinct().prefetch_related('specifications').filter(filters)

Теперь я могу l oop над категориями и показать связанные спецификации, такие как:

{% for category in categories %}
    <tr>
        <th colspan="7">{{ category.name }} - ({{ category.abbr }})</th>
    </tr>
    {% for specification in category.specifications.all %}
    ...

Я также хочу использовать paginator, но теперь paginator учитывает только категории, а не соответствующие спецификации. Можно ли разбить на страницы спецификации по данному запросу или мне нужно изменить запрос, чтобы получить все спецификации?

Ответы [ 3 ]

1 голос
/ 20 апреля 2020

Использование Предварительная выборка

categories = Category.objects.distinct().filter(filters)
category_ids = categories.values_list('id', flat=True) # category ids on page
categories = categories.prefetch_related(
    Prefetch(
        'specifications',
        queryset=Specialization.objects.filter(category_id__in=category_ids)
    )
)

Здесь он создает еще один запрос БД (для получения идентификаторов категорий), но он будет стоить меньше, чем предварительная выборка всех специализаций, я думаю. Это зависит от вашей структуры данных, но это одно из решений.

0 голосов
/ 30 апреля 2020

То, что вы пытаетесь достичь, - это анти-паттерн prefetch_related. Предварительная выборка - это выборка «всех» связанных строк, но вариант использования - это разбиение на страницы specifications.

Предварительная выборка будет полезна в тех случаях, когда число связанных строк в родительской строке составляет ~ 5 (или до 10, помните, что вы тратите пропускную способность сети БД, увеличивая предварительно выбранные дочерние строки. Поэтому, если вы рассматриваете разбиение на страницы, Лучше всего избегать предварительной выборки)

Примечание. Дочерние строки = спецификации, Родительские строки = категории, в вашем случае использования.

Мой ответ будет состоять в том, чтобы избежать использования предварительной выборки в этом случае. Просто используйте следующее

categories = Category.objects.distinct().filter(filters)
--
{% for category in categories %}
<tr>
    <th colspan="7">{{ category.name }} - ({{ category.abbr }})</th>
</tr>
# Use some table lib like django-tables 
...

Если это простой внутренний проект, выполните go с предварительной загрузкой, без проблем, иначе вы столкнетесь с проблемами производительности БД.

0 голосов
/ 30 апреля 2020

Вы пробовали django -tables2 ?

С его помощью вы можете просто визуализировать таблицу с чем-то вроде:

Создайте класс CategoryTable и добавьте его на ваш взгляд:

class CategoryTable(tables.Table):
    class Meta:
        model = Category


def your_view(request):
    ...
    categories = Category.objects.distinct()
                                 .prefetch_related('specifications')
                                 .filter(filters)
    table = CategoryTable(categories)
    table.paginate(page=request.GET.get("page", 1), per_page=25)
    return render(request, "category_template.html", {"table": table})

Затем в вашем шаблоне просто введите:

{% load django_tables2 %}
{% render_table table %}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...