Django пагинация с POST-запросами
Мне нужно реализовать пагинацию в django v3.0.2. Есть одна страница, на которой пользователь выбирает в селектах различные параметры и нажимает кнопку "Поиск". Вот часть кода из шаблона страницы 1:
<form novalidate id="myForm" method="POST" action="{% url 'select_params' %}">
{% csrf_token %}
<select class="dm" multiple="" name="t" required="" id="tSelect">
{% for ts in ts_all %}
<option value="{{ ts.id }}">{{ ts.runame }}</option>
{% endfor %}
</select>
<select class="dm" multiple="" name="c" required="" id="cSelect">
{% for cm in cm_all %}
<option value="{{ cm.id }}">{{ cm.runame }}</option>
{% endfor %}
</select>
<button type="submit" class="sd-button" id="sd-button">Поиск</button>
</form>
Пользователь должен попасть на новую страницу, где ему выдаются объекты согласно POST-запросу(стр.1) и на эти объекты работает пагинация. Вот часть кода из шаблона страницы 2:
{% for d in ds %}
{{ d.runame }}
{% endfor %}
{% if ds.has_other_pages %}
<ul class="pagination">
{% if ds.has_previous %}
<li><a href="?page={{ ds.previous_page_number }}">«</a></li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in ds.paginator.page_range %}
{% if ds.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li><a href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if ds.has_next %}
<li><a href="?page={{ ds.next_page_number }}">»</a></li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
Вот функция во views.py, которая должна создавать пагинацию для страницы 2:
def search_params(request, page_number=1):
q = request.POST
c_value = q.getlist('c')
t_value = q.getlist('t')
c_value = c_value[-1] if c_value else C.objects.values().last()['id']
t_value = t_value if t_value else T.objects.values_list('id', flat=True)
ds_query = Dis.objects.filter(dis_c__ct__lte=c_value).filter(ts__id__in=taste_value).distinct()
paginator = Paginator(ds_query, 2)
page = request.GET.get('page')
ds = paginator.get_page(page_number)
context = {'ds':ds}
return render(request, 'funcs/d_query.html', context)
Вот urls.py:
urlpatterns = [
path('', views.index, name='index'),
path('dises', views.dises, name='dises'), #стр1
re_path(r'^d_query/(\d+)/$', views.search_params, name='select_params'),#стр2
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Проблемы:
1) В теге <form> на стр1 указан параметр action="{% url 'select_params' %}", который в таком виде не работает, требует reverse match.
2) Сама пагинация отображается, если добавить цифру, например "1" к параметру action="{% url 'select_params' 1 %}", но механизм пагинации не работает. Например, по нажатию на цифру "2" на странице с пагинацией - подгружается текущая страница, но вообще без объектов из БД.
Вопрос: Kак правильно организовать пагинацию в django, где есть форма с POST-запросом? Приведите, по возможности, примеры кода или поправьте мой. Буду вам премногоблагодарен, ибо впервые делаю пагинацию :)
*догадываюсь, что что-то надо во вьюхе делить на POST и GET, но не до конца соображаю
Ответы (1 шт):
Сделал, чувачеллы)
В общем-то, как и предполагалось, нужно отдельно обработать GET-запрос в принимающей функции. Не уверен, что моё решение оптимально по ресурсам, но оно работает.
Что я сделал:
1) Добавляем в функцию во views.py обработку условий if request.method == 'POST' и if request.method == 'GET'.
2) Добавляем в сессию запроса новый параметр (далее в коде - search), в который сохраняем все выбранные пользователем параметры из POST-запроса.
3) При обработке GET-запроса(то есть пагинации) используем эти сохраненные данные из сессии, чтобы пагинатор выгрузил из базы данных нужные записи.
4) Всё, что я писал в вопросе про action="{% url 'select_params' 1 %}" забываем и меняем обратно на action="{% url 'select_params' %}".
Теперь сам код. views.py в упрощенном виде:
def search_params(request, page=1):
if request.method == 'POST':
q = request.POST
c_value = q.getlist('c')
request.session['search'] = f'{c_value}' #это f-строка если чё
ds_query = Dis.objects.filter(dis_c__ct__lte=c_value).distinct()
paginator = Paginator(ds_query, 2)
page = request.GET.get('page')
ds = paginator.get_page(page)
context = {'ds':ds}
return render(request, 'funcs/d_query.html', context)
else:
if 'search' in request.session:
ds_query = Dis.objects.filter(dis_c__ct__lte=request.session['search']).distinct()
paginator = Paginator(ds_query, 2)
page = request.GET.get('page')
ds = paginator.get_page(page)
context = {'ds':ds}
return render(request, 'funcs/d_query.html', context)
В urls.py убираем всю чушь с re_path:
urlpatterns = [
path('', views.index, name='index'),
path('dises', views.dises, name='dises'), #стр1
path('d_query', views.search_params, name='select_params'), #стр2 измененный url
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Вот, как-то так, работает замечательно. При этом не могу сказать насчёт нагрузки на БД в таком случае, т.к. не знаю работу пагинатора изнутри. Если вы знаете более оптимальный и каноничный вариант - пишите, будет клёво изучить!