Фильтры и пагинация с помощью Django Function-Based Views
Я работаю над своей первой программой на Django и хотел бы создать шаблон, включающий функции фильтрации и пагинации, которые можно было бы использовать одновременно. Вот что у меня есть на данный момент:
models.py
class Player (models.Model):
name = models.CharField(max_length=32)
surname = models.CharField(max_length=32)
def __str__(self):
return f"{self.name} {self.surname}"
class Meta:
verbose_name_plural = "Players"
views.py
def GetPlayers(request):
players = Player.objects.all().values()
pn = request.GET.get('name')
if pn != '' and pn is not None:
players = players.filter(name__icontains=pn)
page_num = request.GET.get('page', 1)
paginator = Paginator(players, 2)
page_obj = paginator.get_page(page_num)
template = loader.get_template('players.html')
context = {
'players' : players,
'page_obj' : page_obj,
}
return HttpResponse(template.render(context, request))
players.html
{% block content %}
<div class="mycard">
<h1>Players</h1>
<div class="filters">
<form action="" method="GET">
<div class="row">
<div class="col-xl-3">
<label>Name:</label>
<input type="text" class="form-control" placeholder="name" name="name" {% if name %} value = "{{ name }}" {% endif %}>
</div>
<div class="col-xl-2" style="padding-top: 2%;">
<button type="submit" class="btn custom-btn">Filter</button>
</div>
</div>
</form>
</div>
<p/>
<div style="overflow-x:auto;">
<table>
<thead>
<th>Name</th>
<th>Surname</th>
</thead>
<tbody>
{% for x in page_obj %}
<td>{{ x.name }}</td>
<td>{{ x.surname }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include "pagination.html" %}
</div>
</div>
{% endblock %}
Представим, что у меня есть 6 игроков по имени «Джон», «Марк», «Фил», «Джейсон», «Джейн» и «Джульетта». Если не применять никакой фильтрации по именам, они будут сгруппированы следующим образом:
- Страница 1: «Джон» и «Марк»
- Страница 2: «Фил» и «Джейсон»
- Страница 3: «Джейн» и «Джульетта»
Исходя из этого, если я буду фильтровать по именам, содержащим «J» в текстовом поле фильтра, я ожидаю получить:
- Страница 1: «Джон» и «Джейсон»
- Страница 2: «Джейн» и «Джульетта»
Страница 1 работает как положено, но когда я нажимаю на ссылку для перехода на страницу 2, фильтр, похоже, сбрасывается, и я получаю «Фила» и «Джейсона»; кроме того, становится доступной ссылка на третью страницу, включающая «Джейн и Джульетту», как будто фильтр вообще не применялся.
Как сохранить фильтры, применяемые при навигации по страницам кверисета?
Вам нужно добавить свой фильтр в качестве параметра запроса к URL пагинации. Для этого необходимо, чтобы он находился внутри контекста.
views.py (небольшое изменение, чтобы сократить и сделать его более чистым)
def GetPlayers(request):
players = Player.objects.all().values()
pn = request.GET.get("name", None)
if pn:
players = players.filter(name__icontains=pn)
paginator = Paginator(players, 2)
page_num = request.GET.get("page")
page_obj = paginator.get_page(page_num)
ctx = {"players": players, "page_obj": page_obj, "pn": pn}
return render(request, "players.html", ctx)
pagination.html
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1{% if pn %}&name={{pn}}{% endif %}">« first</a>
<a href="?page={{ page_obj.previous_page_number }}{% if pn %}&name={{pn}}{% endif %}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% if pn %}&name={{pn}}{% endif %}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}{% if pn %}&name={{pn}}{% endif %}">last »</a>
{% endif %}
</span>
</div>
Примечание: В качестве хорошей практики (соглашения об именах) функции и переменные часто называются в snake_case