Django:将空结果页面重定向到根页面的最佳方法
Django: best way to redirect empty results pages to root page
我有一个应用程序可以在 Django 列表视图中分页和显示对象。我在列表视图中使用 paginate_by=20
。
当我有 20 多个对象并且 Google 索引第二个结果页面时 /results/?page=2
。然后当结果低于 20 个对象时,第二页将变为 404。
当第 2 页、第 3 页等 return 没有任何结果时,将第 2 页重定向到 /results/
的最佳方法是什么。
我考虑过编写一个自定义 404 视图来删除 URL 参数,但我想最好在列表视图本身中捕获它?
这是我的列表视图:
class ListingListView(ListView):
ORDER_BY_MAP = {
None: {
'directive': ListingsManager.CATEGORY_DEFAULT_ORDER,
},
'title': {
'label': 'Title A > Z',
'directive': ('title',),
},
'-title': {
'label': 'Title Z > A',
'directive': ('-title',),
},
'rating': {
'label': 'Rating',
'directive': ('-aggregate_rating', '-reviews_count'),
},
}
template_name = 'directory/listing_list.html'
context_object_name = 'listings'
paginate_by = 20
@cached_property
def country(self):
if 'country_slug' in self.kwargs:
country = get_object_or_404(Country, slug=self.kwargs['country_slug'])
set_user_language(self.request, country.language)
return country
else:
return None
@cached_property
def category(self):
if 'category_slug' in self.kwargs:
return get_object_or_404(Category.objects.select_related('parent').prefetch_related(
'children'), slug=self.kwargs['category_slug'])
else:
return None
@cached_property
def region(self):
if 'region_slug' in self.kwargs:
return get_object_or_404(Region, slug=self.kwargs['region_slug'])
else:
return None
def get_url_params(self):
return {k: v[0] for k, v in dict(self.request.GET).items()}
def get_queryset(self):
url_params = self.get_url_params()
user_order_by = url_params.get('order_by', None)
if user_order_by not in self.ORDER_BY_MAP:
user_order_by = None
country = self.country
category = self.category
region = self.region
queryset = Listing.objects.category_view(category, country, region)
return queryset.order_by(*self.ORDER_BY_MAP[user_order_by]['directive']).distinct()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
country = self.country
region = self.region
listings = self.get_queryset()
context['category'] = category = self.category
if region:
context['title'] = f'{category.name} in {region.name}'
else:
context['title'] = category.name
# Add information for the category sidebar
context['up_links'] = []
if not category.is_root:
root_category = Category.objects.root()
context['up_links'].append({
'name': root_category.name,
'link': reverse('directory-category', args=[country.slug, root_category.slug])
})
if category.parent:
context['up_links'].append({
'name': category.parent.name,
'link': reverse('directory-category', args=[country.slug, category.parent.slug])
})
if region:
context['up_links'].append({
'name': category.name,
'link': reverse('directory-category', args=[country.slug, category.slug])
})
if category.has_regions:
context['region_links'] = {}
regions = Region.objects.filter(listings__categories=category, listings__countries=country).order_by('name')
for item in regions:
context['region_links'][item.name] = reverse('directory-region',
args=[country.slug, category.slug, item.slug])
context['subcategories'] = []
if category.is_root:
children = Category.objects.filter(parent__isnull=True).exclude(is_root=True)
else:
children = category.children
for c in children.order_by('nav_menu_order'):
context['subcategories'].append({
'name': c.name,
'thumbnail_image': c.thumbnail_image,
'link': reverse('directory-category', args=[country.slug, c.slug]),
})
# Do not show recommended badge in root category view
context['show_recommended_badge'] = not category.is_root
# Add sorting menu
context['order_by_URLs'] = {}
for key, value in self.ORDER_BY_MAP.items():
if key:
context['order_by_URLs'][value['label']] = f'{self.request.path}?order_by={key}'
# Add total listings count (template only counts listings on that page, not total)
context['listings_count'] = listings.count()
# Add sorting parameters so we can preserve sorting in pagination links
url_params = self.request.GET.copy()
url_params.pop('page', None)
context['url_params'] = url_params
return context
您可以通过<ModelName>.objects.all().count()
查看模型的长度是否超过20(这是分页值)。根据这个值,您可以执行 if/else 语句并呈现视图。呈现页面本身 /results/
或仅重定向到主页(或任何其他页面)。如果能发下views.py
的代码就好了
试一试,在你的 ListingListView
中将 paginate_by = 20
替换为 list_per_page = 10
,这是为了覆盖默认的分页器。
并在 get_context_data
方法中:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
listings = Listing.objects.all() # change this according to your requirement
# paginate the listings
paginator = Paginator(listings, self.list_per_page)
page = self.request.GET.get('page')
try:
paged_listings = paginator.page(page)
except EmptyPage:
# in case if you got an empty page this will show the fist page
paged_listings = paginator.page(1)
context['paged_listings'] = paged_listings
return context
您必须导入 Paginator class and EmptyPage 异常
from django.core.paginator import Paginator, EmptyPage
注意:由于 paged_listings
是上下文名称,您必须在模板中使用此上下文名称才能查看分页列表
谢谢大家的回答。
我最终做的是编写一个覆盖 get
方法的混合。这样我就可以将它重新用于所有分页的 ListView,而无需自定义分页器。
这是混音:
class SafePaginateMixin(object):
def get(self, *args, **kwargs):
try:
return super().get(*args, **kwargs)
except Http404:
page = self.request.GET.get('page', None)
if page:
return redirect(self.request.path)
else:
raise
然后将 mixin 添加到我的 ListView
:
class ListingListView(SafePaginateMixin, ListView)
如果 404 是由带有 page
URL 参数的 URL 引发的,则重定向到请求路径。如果没有,请提出404。
我有一个应用程序可以在 Django 列表视图中分页和显示对象。我在列表视图中使用 paginate_by=20
。
当我有 20 多个对象并且 Google 索引第二个结果页面时 /results/?page=2
。然后当结果低于 20 个对象时,第二页将变为 404。
当第 2 页、第 3 页等 return 没有任何结果时,将第 2 页重定向到 /results/
的最佳方法是什么。
我考虑过编写一个自定义 404 视图来删除 URL 参数,但我想最好在列表视图本身中捕获它?
这是我的列表视图:
class ListingListView(ListView):
ORDER_BY_MAP = {
None: {
'directive': ListingsManager.CATEGORY_DEFAULT_ORDER,
},
'title': {
'label': 'Title A > Z',
'directive': ('title',),
},
'-title': {
'label': 'Title Z > A',
'directive': ('-title',),
},
'rating': {
'label': 'Rating',
'directive': ('-aggregate_rating', '-reviews_count'),
},
}
template_name = 'directory/listing_list.html'
context_object_name = 'listings'
paginate_by = 20
@cached_property
def country(self):
if 'country_slug' in self.kwargs:
country = get_object_or_404(Country, slug=self.kwargs['country_slug'])
set_user_language(self.request, country.language)
return country
else:
return None
@cached_property
def category(self):
if 'category_slug' in self.kwargs:
return get_object_or_404(Category.objects.select_related('parent').prefetch_related(
'children'), slug=self.kwargs['category_slug'])
else:
return None
@cached_property
def region(self):
if 'region_slug' in self.kwargs:
return get_object_or_404(Region, slug=self.kwargs['region_slug'])
else:
return None
def get_url_params(self):
return {k: v[0] for k, v in dict(self.request.GET).items()}
def get_queryset(self):
url_params = self.get_url_params()
user_order_by = url_params.get('order_by', None)
if user_order_by not in self.ORDER_BY_MAP:
user_order_by = None
country = self.country
category = self.category
region = self.region
queryset = Listing.objects.category_view(category, country, region)
return queryset.order_by(*self.ORDER_BY_MAP[user_order_by]['directive']).distinct()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
country = self.country
region = self.region
listings = self.get_queryset()
context['category'] = category = self.category
if region:
context['title'] = f'{category.name} in {region.name}'
else:
context['title'] = category.name
# Add information for the category sidebar
context['up_links'] = []
if not category.is_root:
root_category = Category.objects.root()
context['up_links'].append({
'name': root_category.name,
'link': reverse('directory-category', args=[country.slug, root_category.slug])
})
if category.parent:
context['up_links'].append({
'name': category.parent.name,
'link': reverse('directory-category', args=[country.slug, category.parent.slug])
})
if region:
context['up_links'].append({
'name': category.name,
'link': reverse('directory-category', args=[country.slug, category.slug])
})
if category.has_regions:
context['region_links'] = {}
regions = Region.objects.filter(listings__categories=category, listings__countries=country).order_by('name')
for item in regions:
context['region_links'][item.name] = reverse('directory-region',
args=[country.slug, category.slug, item.slug])
context['subcategories'] = []
if category.is_root:
children = Category.objects.filter(parent__isnull=True).exclude(is_root=True)
else:
children = category.children
for c in children.order_by('nav_menu_order'):
context['subcategories'].append({
'name': c.name,
'thumbnail_image': c.thumbnail_image,
'link': reverse('directory-category', args=[country.slug, c.slug]),
})
# Do not show recommended badge in root category view
context['show_recommended_badge'] = not category.is_root
# Add sorting menu
context['order_by_URLs'] = {}
for key, value in self.ORDER_BY_MAP.items():
if key:
context['order_by_URLs'][value['label']] = f'{self.request.path}?order_by={key}'
# Add total listings count (template only counts listings on that page, not total)
context['listings_count'] = listings.count()
# Add sorting parameters so we can preserve sorting in pagination links
url_params = self.request.GET.copy()
url_params.pop('page', None)
context['url_params'] = url_params
return context
您可以通过<ModelName>.objects.all().count()
查看模型的长度是否超过20(这是分页值)。根据这个值,您可以执行 if/else 语句并呈现视图。呈现页面本身 /results/
或仅重定向到主页(或任何其他页面)。如果能发下views.py
试一试,在你的 ListingListView
中将 paginate_by = 20
替换为 list_per_page = 10
,这是为了覆盖默认的分页器。
并在 get_context_data
方法中:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
listings = Listing.objects.all() # change this according to your requirement
# paginate the listings
paginator = Paginator(listings, self.list_per_page)
page = self.request.GET.get('page')
try:
paged_listings = paginator.page(page)
except EmptyPage:
# in case if you got an empty page this will show the fist page
paged_listings = paginator.page(1)
context['paged_listings'] = paged_listings
return context
您必须导入 Paginator class and EmptyPage 异常
from django.core.paginator import Paginator, EmptyPage
注意:由于 paged_listings
是上下文名称,您必须在模板中使用此上下文名称才能查看分页列表
谢谢大家的回答。
我最终做的是编写一个覆盖 get
方法的混合。这样我就可以将它重新用于所有分页的 ListView,而无需自定义分页器。
这是混音:
class SafePaginateMixin(object):
def get(self, *args, **kwargs):
try:
return super().get(*args, **kwargs)
except Http404:
page = self.request.GET.get('page', None)
if page:
return redirect(self.request.path)
else:
raise
然后将 mixin 添加到我的 ListView
:
class ListingListView(SafePaginateMixin, ListView)
如果 404 是由带有 page
URL 参数的 URL 引发的,则重定向到请求路径。如果没有,请提出404。