Django, django-filter and pagination



我的目标是有一个'user_profile'页面,显示感兴趣的用户的相关信息。

此外,'user_profile'页面应该包含由相应用户创建的所有文章作为新博客条目。

但是,这些帖子应该可以通过应用程序'django-filter'进行过滤,并进行分页。目前,我很难对过滤后的帖子进行分页。所以我的问题是如何实现后者?到目前为止,我使用了以下方法:

filters.py

import django_filters
class AccountPostFilter(django_filters.FilterSet):
title = django_filters.CharFilter(lookup_expr='icontains')
category = django_filters.ChoiceFilter(choices=cat_list)
class Meta:
model = Post
fields = ['title', 'category']

views.py

class UserProfile(DetailView, MultipleObjectMixin):
model = Account
template_name = 'account/user_profile.html'
paginate_by = 5

def get_context_data(self, **kwargs):
posts = Post.objects.all().filter(author=self.kwargs['pk'])
context = super().get_context_data(object_list=posts, **kwargs)
context['filterset'] = AccountPostFilter(self.request.GET, queryset=posts)
return context
非常感谢您的宝贵时间。最好的祝愿,丹尼尔

还有另一种方法,而且是一种干净、专业的方式,它将为你省去使用Django过滤器的麻烦:

创建一个名为clean_filters的辅助函数(这将帮助您清理过滤器)来自browser:

def clean_filters(filters):
filters = {k: v for k, v in filters.items() if v}
return filters

创建另一个名为search的帮助函数(这将帮助您从get请求获取参数)把它们放在django filter指令中的**过滤器中。和一起返回分页器所以当你从一个页面移动到另一个页面时,你可以保持相同的过滤器):

from 'your_utils_file' import clean_filters
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def search(request):
filters = {
"account__first_name__icontains": request.GET.get("fname_kw"), # put your filters here
"account__last_name__icontains": request.GET.get("lname_kw"), # put your filters here
}
html_queries = {
"fname_kw": request.GET.get("fname_kw"),
"lname_kw": request.GET.get("lname_kw"),
}
filters = clean_filters(filters)
html_queries = clean_filters(html_queries)
posts = Post.objects.filter(**filters) # put your model here
page = request.GET.get('page', 1)
paginator = Paginator(posts, 8)
try:
posts= paginator.page(page)
except PageNotAnInteger:
posts= paginator.page(1)
except EmptyPage:
posts= paginator.page(paginator.num_pages)
return posts

这是你的视图(这只是调用搜索函数来减少你的视图的代码,使你的代码更容易维护):

def search_page(request):
posts = search(request)
if posts is not None:
context = {
'posts': posts,
}
return render(request, "core/index.html", context)
return redirect("index")

这是你的HTML(只是Django和Bootstrap的经典分页代码)。在GET请求内的循环中也有过滤器和过滤器的值):

<div class="mb-5">
{% if posts.has_other_pages %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
{% if posts.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ posts.previous_page_number }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}" tabindex="-1">
<i class="fa fa-angle-left"></i>
<span class="sr-only">Prev</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="javascript:void(0)" tabindex="-1">
<i class="fa fa-angle-left"></i>
<span class="sr-only">Prev</span>
</a>
</li>
{% endif %}
{% for i in posts.paginator.page_range %}
{% if posts.number == i %}
<li class="page-item active"><a class="page-link" href="javascript:void(0)">{{ i }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ posts.next_page_number }}{% for fil, fil_value in filters.items %}&{{fil}}={{fil_value}}{% endfor %}">
<i class="fa fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="javascript:void(0)">
<i class="fa fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}  
</ul>
</nav>
{% endif %}

这是一个老问题,但是,也许有人想要以一种像我一样的基本方式实现这一点。

但我不能保证它会很完美。在我的情况下,这个分页页面仅供员工使用,我们没有给予太多关注。但它是有效的。我用js解决了这个问题。

经典的过滤器集和分页视图方法:

# myview.py
# will be used to apply filters on UI.
filterset = MyFilterSet(
request.GET, queryset=MyModel.objects.all()
)
# divided by 20 object per page.
paginator = Paginator(filterset.qs, 20)
page = request.GET.get('page')
# will be used to apply pagination and iteration of objects.
try:
paginated_objects = paginator.page(page)
except PageNotAnInteger:
paginated_objects = paginator.page(1)
except EmptyPage:
paginated_objects = paginator.page(paginator.num_pages)
context = {
"filter": filterset,
"paginated_objects": paginated_objects
}

滤波器形式:

<!-- mytemplate.html FILTER FORM -->
<form method="get" id="djangoFilterSetForm">
<div class="row">
{% for filter_form in filter.form %}
<div class="col">
<label>{{ filter_form.label }}</label>
{{ filter_form }}
</div>
{% endfor %}
</div>
{% if show_submit_button %}
<button type="submit">
Submit Filter
</button>
{% endif %}
<a href="{{ request.path }}">
Reset Filters
</a>
</form>

分页:

<!-- mytemplate.html PAGINATION -->
<div id="pagination_container">
<span class="page-links">
{% if paginated_objects.has_previous %}
<button type="button" onclick="navigateUrl('{{ request.get_full_path }}', {{ paginated_objects.previous_page_number }})">Previous</button>
{% endif %}
<span class="page-current">
{{ paginated_objects.number }} / {{ paginated_objects.paginator.num_pages }}
</span>
{% if paginated_objects.has_next %}
<button type="button" onclick="navigateUrl('{{ request.get_full_path }}', {{ paginated_objects.next_page_number }})">Previous</button>
{% endif %}
</span>
</div>

对象迭代:

<!-- mytemplate.html QUERYSET ITERATION -->
{% for my_object in paginated_objects %}
<!-- ... doing something with my_object ... -->
{% endfor %}

JS解决方案:

<!-- mytemplate.html JS SOLUTION -->
<script type="text/javascript">
function arrangePageQuery(urlPath, pageToNavigate) {
if (window.location.href.includes("?" )) {
// there's filter queries in url:
window.location.href = urlPath + "&page=" + pageToNavigate;
} else {
// there's no filter queries:
window.location.href = urlPath + "?page=" + pageToNavigate;
}
}
function navigateUrl(fullPath, pageToNavigate) {

if (window.location.href.includes("?page=")) {
// to avoid appending when there is no filter queries, remove page query:
let baseFullPath = window.location.href.split("?page=")[0]
window.location.href = baseFullPath + "?page=" + pageToNavigate;
return
}
if (window.location.href.includes("&page=")) {
// to avoid appending when there is filter queries, remove page query
let baseFullPath = window.location.href.split("&page=")[0]
arrangePageQuery(baseFullPath, pageToNavigate);
} else {
arrangePageQuery(fullPath, pageToNavigate)
}
}
</script>

最新更新