Evgenii Legotckoi
28 февраля 2018 г. 15:57

Django - Урок 033. Передача списка аргументов в метод order_by для сортировки QuerySet

Для улучшения удобства использования разделов статей была внедрена сортировка статей по дате, заголовку, и количеству просмотров. Помимо этого была добавлена возможность поиска информации по статьям раздела. Реализуется эта возможность через несколько чекбоксов, которые добавляют названия колонок для сортировки в URL страницы, соответственно страница перезагружается.

Например, в модели данных есть несколько колонок

  • title
  • pub_date
  • views

По ним и будем делать сортировку, которая в обычном варианте запроса выглядела бы так

  1. Article.objects.all().order_by('title', 'pub_date', 'views')

Но поскольку мы используем чекбоксы, то варианты сортировку могу присутствовать, а могу и отсутствовать. Но не будем же мы писать if else блоки на каждую комбинацию чекбоксов? Конечно, нет.


Посмотрим для начала, как может быть написана форма для реализации сортировки. Сразу оговорюсь, что приведу вариант формы без стилизации, которая применена у меня на сайте. Дело в том, что для этого используется Bootstrap 4 Material Design, что несколько усложняем вариант вёрстки и добавит ряд лишних элементов в пример.

  1. <form method="get">
  2. <button type="submit" class="btn btn-sm btn-primary btn-raised mr-3">{% trans 'Сортировать' %}</button>
  3. <input name="sort" type="checkbox" value="title" {{ by_title }}>{% trans "по заголовку" %}
  4. <input name="sort" type="checkbox" value="pub_date" {{ by_date }}>{% trans "по дате" %}
  5. <input name="sort" type="checkbox" value="views" {{ by_views }}>{% trans "по просмотрам" %}
  6. </form>

Как видите, все чекбоксы в коде имеют имя sort, а значение value будет равно имени колонки, по которой можно будет включить сортировку.

Таким образом в URL будут фигурировать следующие аргументы

  1. ?sort=title&sort=pub_date&sort=views

Django позволяет извлечь все аргументы из запроса в качестве списка, который мы можем передать в метод order_by, чтобы выполнить сортировку.

И фактически представление для рендеринга раздела со статьями может выглядеть следующим образом

  1. class SectionView(View):
  2.  
  3. def get(self, request, slug):
  4. section = get_object_or_404(Section, slug=slug)
  5. sort = request.GET.getlist('sort')
  6. articles = section.article_set.all().order_by(*sort)
  7.  
  8. return render(
  9. request=request,
  10. template_name='knowledge/section.html',
  11. context={
  12. 'section': section,
  13. 'articles': articles
  14. }
  15. )

Обратите внимание, на то, что вместо метода get используется метод getlist , который возвращает список значений аргументов, если в запросе фигурировало одно и тоже имя аргумента несколько раз.

  1. sort = request.GET.getlist('sort')

А далее с помощью указателя передаём список в качестве аргументов в метод order_by

  1. articles = section.article_set.all().order_by(*sort)

Для Django рекомендую VDS-сервера хостера Timeweb .

Вам это нравится? Поделитесь в социальных сетях!

bernar92
  • 17 мая 2018 г. 9:53
можно же это реализовать через django-filter!
Evgenii Legotckoi
  • 17 мая 2018 г. 12:05

Ну. Массово пока не использую фильтрации, поэтому не искал батареек. Так получилось, что даже не знал про django-filters.

Спасибо. Можете черкануть заметку, как аналог этой статье ;-)
bernar92
  • 17 мая 2018 г. 21:16

хорошие статьи я много чего нашел тут интересного и нового... мне нравиться!
если вдруг интересно будет по фильтрам вот примерный код)

import django_filters
from .models import Product

CHOICES =[
        ["name", "по алфавиту"],
        ["price", "дешевые сверху"],
        ["-price", "дорогие сверху"]
]


class ProductFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(name='name', lookup_expr='icontains')
    category__slug = django_filters.CharFilter()
    price__gt = django_filters.NumberFilter(name='price', lookup_expr='gt')
    price__lt = django_filters.NumberFilter(name='price', lookup_expr='lt')
    ordering = django_filters.OrderingFilter(choices=CHOICES, required=True, empty_label=None,)

    class Meta:
        model = Product
        exclude = [field.name for field in Product._meta.fields]
        order_by_field = 'name'


from django_filters.views import FilterView
class EnumerationListView(FilterView):
    template_name = '.html'
    model = Lot
    paginate_by = 50
    filterset_class = ProductFilter
    context_object_name = 'product_list'
Evgenii Legotckoi
  • 18 мая 2018 г. 2:23

Спасибо за пример кода.
Когда буду внедрять больше поисковых виджетов на сайт, в первую очередь воспользуюсь вашим примером кода. Благо уже есть некоторые целевые места, где это можно применить.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь