我正在使用Django Forms通过POST进行过滤/分面搜索,我想让Django的paginator类来组织结果。在各个页面之间传递客户端时,如何保留原始请求?换句话说,一旦我将另一个页面的GET请求传递回我的视图,我似乎就丢失了POST数据。我已经看到一些建议使用AJAX只刷新页面的结果块,但我想知道是否有一个Django本机机制来执行此操作。
感谢。
答案 0 :(得分:25)
如果要在以后的请求中访问商店数据,则必须将其存储在某处。 Django提供了几种存档方式:
1)您可以使用sessions来存储查询:访问您网站的每个访问者都会获得一个空的会话对象,您可以在此对象中存储您想要的任何内容,像一个字典。缺点:单个访问者不能同时进行多个分页搜索。
2)使用cookies:如果您设置了存储在客户端的cookie,浏览器会将cookie的数据附加到您可以访问它的每个请求中。 Cookie更加适合服务器,因为您在服务器上不需要会话管理器,但存储在Cookie中的数据对客户端是可见的(并且可编辑)。缺点:和以前一样。
3)使用隐藏字段:您可以在搜索结果页面上添加包含一些隐藏字段的表单,并将查询存储在其中。然后,只要您提交表单,客户端就会重新发送查询。缺点:您必须在页面上使用带有提交按钮的表单进行分页(简单链接不起作用)。
4)创建包含查询的链接:您也可以使用GET,而不是使用POST。例如,您可以使用"/search/hello+world/?order=votes"
之类的链接和"/search/hello+world/2/?order-votes"
之类的“分页链接”。然后可以从URL轻松检索查询。缺点:您可以通过GET发送的最大数据量是有限的(但这不应该是简单搜索的问题)。
5)使用组合:您可能希望将所有数据存储在会话或数据库中,并通过生成的密钥访问它们,您可以将其放入URL中。然后,URL可能看起来像“/search/029af239ccd23/2"
(对于第二页),您可以使用该密钥访问之前存储的大量数据。这消除了解决方案1以及解决方案4的缺点新的缺点:很多工作:)
6)使用AJAX:使用ajax,您可以将数据存储在客户端的某些js变量中,然后可以将其传递给其他请求。由于ajax只会更新你的结果列表,因此变量不会丢失。
答案 1 :(得分:5)
从tux21b读取非常好的答案我决定实现第一个选项,即使用会话来存储查询。这是一个搜索房地产数据库的应用程序。这是视图代码(使用django 1.5):
def main_search(request):
search_form = UserSearchForm()
return render(request, 'search/busca_inicial.html', {'search_form': search_form})
def result(request):
if request.method == 'POST':
search_form = UserSearchForm(request.POST)
if search_form.is_valid():
# Loads the values entered by the user on the form. The first and the second
# are MultiChoiceFields. The third and fourth are Integer fields
location_query_list = search_form.cleaned_data['location']
realty_type_query_list = search_form.cleaned_data['realty_type']
price_min = search_form.cleaned_data['price_min']
price_max = search_form.cleaned_data['price_max']
# Those ifs here populate the fields with convenient values if the user
# left them blank. Basically the idea is to populate them with values
# that correspond to the broadest search possible.
if location_query_list == []:
location_query_list = [l for l in range(483)]
if realty_type_query_list == []:
realty_type_query_list = [r for r in range(20)]
if price_min == None:
price_min = 0
if price_max == None:
price_max = 100000000
# Saving the search parameters on the session
request.session['location_query_list'] = location_query_list
request.session['price_min'] = price_min
request.session['price_max'] = price_max
request.session['realty_type_query_lyst'] = realty_type_query_list
# making a query outside the if method == POST. This logic makes the pagination possible.
# If the user has made a new search, the session values would be updated. If not,
# the session values will be from the former search. Of course, that is what we want because
# we want the 'next' and 'previous' pages correspond to the original search
realty_list_result = FctRealtyOffer.objects.filter(location__in=request.session['location_query_list']
).filter(price__range=(request.session['price_min'], request.session['price_max'])
).filter(realty_type__in=request.session['realty_type_query_lyst'])
# Here we pass the list to a table created using django-tables2 that handles sorting
# and pagination for us
table = FctRealtyOfferTable(realty_list_result)
# django-tables2 pagination configuration
RequestConfig(request, paginate={'per_page': 10}).configure(table)
return render(request, 'search/search_result.html', {'realty_list_size': len(realty_list_result),
'table': table})
希望它有所帮助!如果有人有任何改进建议,欢迎。
答案 2 :(得分:4)
作为@rvnovaes,一种使用会话来解决问题的方法。
他的解决方案的缺点是,如果有很多搜索字段,你必须编写许多代码行,而且如果你在结果页面中显示搜索表单,所有字段都将是空白的,而他们应该保留他们的值。
所以我宁愿在会话中保存所有的帖子数据,并且在视图的开头强制request.POST和request.method的值,如果定义了一个会话:
""" ... """
if not request.method == 'POST':
if 'search-persons-post' in request.session:
request.POST = request.session['search-persons-post']
request.method = 'POST'
if request.method == 'POST':
form = PersonForm(request.POST)
request.session['search-persons-post'] = request.POST
if form.is_valid():
id = form.cleaned_data['id']
""" ... """
更多信息here
答案 3 :(得分:0)
您可以询问请求对象是否为ajax,只需request.is_ajax
。通过这种方式,您可以检测到它是第一次发布请求还是有关下一页的更多问题。
答案 4 :(得分:0)
将搜索表单和结果显示在一个django模板上。最初,使用css隐藏结果显示区域。在POST表单时,您可以检查搜索是否返回任何结果,并在结果存在时使用css隐藏搜索表单。如果结果不存在,请使用css像以前一样隐藏结果显示区域。在您的分页链接中,使用javascript提交表单,这可能就像document.forms[0].submit(); return false;
您需要处理如何将页码传递给django的分页引擎。
答案 5 :(得分:0)
我在Web应用程序中使用get参数做到了这一点也许我可以为您提供帮助:
Views.py
class HomeView(ListView):
model = Hotel
template_name = 'index.html'
paginate_by = 10 # if pagination is desired
def get_queryset(self):
qs = super().get_queryset()
kwargs = {}
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
kwargs['title__icontains'] = title
if 'category' in self.request.GET:
category = self.request.GET.get('category')
if category:
kwargs['category_id'] = category
if 'size' in self.request.GET:
size = self.request.GET.get('size')
if size:
kwargs['size_id'] = size
if 'service' in self.request.GET:
service = self.request.GET.get('service')
if service:
kwargs['service_id'] = service
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
if ownership:
kwargs['ownership_id'] = ownership
qs = qs.filter(**kwargs)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form_init = {}
form = SearchForm()
if self.request.GET.items():
try:
parameters = self.request.GET.items()
except KeyError:
parameters = {}
for key, value in parameters:
for field in form.fields:
if key == field:
form_init[key] = value
form.initial = form_init
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
context.update({
'title': title
})
if 'category' in self.request.GET:
category = self.request.GET.get('category')
context.update({
'category': category
})
if 'size' in self.request.GET:
size = self.request.GET.get('size')
context.update({
'size': size
})
if 'service' in self.request.GET:
service = self.request.GET.get('service')
context.update({
'service': service
})
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
context.update({
'ownership': ownership
})
context.update({
'search_form': form
})
return context
分页文件html
<div class="row">
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Previous</span></li>
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.next_page_number }}">Next</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
答案 6 :(得分:0)
我的建议是使用会话或cookie存储发帖请求。如果帖子数据敏感,则应使用会话进行存储。下面的代码包含我使用会话实现它的逻辑。
def index(request):
is_cookie_set = 0
# Check if the session has already been created. If created, get their values and store it.
if 'age' in request.session and 'sex' in request.session:
age = request.session['age']
sex = request.session['sex']
is_cookie_set = 1
else:
# Store the data in the session object which can be used later
request.session['age'] = age
request.session['sex'] = sex
if(request.method == 'POST'):
if(is_cookie_set == 0): # form submission by the user
form = EmployeeForm(request.POST)
sex = form.cleaned_data['sex']
age = form.cleaned_data['age']
if form.is_valid():
result = Employee.objects.all(sex=sex,age_gte=age) # filter all employees based on sex and age
else: # When the session has been created
result = Employee.objects.all(sex=sex,age_gte=age)
paginator = Paginator(result, 20) # Show 20 results per page
page = request.GET.get('page')
r = paginator.get_page(page)
response = render(request, 'app/result.html',{'result':result})
return response
else:
form = EmployeeForm()
return render(request,'app/home.html',{'form':form})
您还应该检查发布字段是否为空,并根据其更改逻辑。您也可以按照@abidibo的建议将整个帖子请求存储在会话中。
您也可以使用Cookie。我已经解释了here