【问题标题】:Django: best way to redirect empty results pages to root pageDjango:将空结果页面重定向到根页面的最佳方法
【发布时间】:2021-06-29 03:28:28
【问题描述】:

我有一个在 Django 列表视图中分页和显示对象的应用程序。我在列表视图中使用paginate_by=20

当我有 20 多个对象并且 Google 索引第二个结果页面 /results/?page=2 时。然后,当结果低于 20 个对象时,第二页会转到 404。

当页面 2、3 等不返回任何结果时,将页面 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

【问题讨论】:

    标签: django listview http-status-code-404


    【解决方案1】:

    试一试,在您的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 类和EmptyPage 异常

    from django.core.paginator import Paginator, EmptyPage
    

    注意:由于paged_listings 是上下文名称,您必须在模板中使用此上下文名称才能查看分页列表

    【讨论】:

      【解决方案2】:

      感谢大家的回答。

      我最终做的是编写一个覆盖get 方法的mixin。这样我就可以对所有分页的 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。

      【讨论】:

        【解决方案3】:

        您可以通过<ModelName>.objects.all().count()查看模型的长度是否超过20(这是分页值)。根据这个值,您可以执行 if/else 语句并呈现视图。要么呈现页面本身/results/,要么只是重定向到主页(或任何其他页面)。能发views.py的code就好了

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-26
          • 2013-12-12
          • 2021-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多