8

我试图弄清楚是否可以在我的查询集上进行查看后处理,然后在使用 django-endless-pagination 进行无限滚动的 django 模板中渲染它。

我有特定于视图的逻辑,它根据上下文省略查询集中的某些结果,以及向列表中的对象添加属性以供模板使用。此逻辑不能通过 SQL 执行,因为它不是模型固有的。它必须在python中完成。

使用 django-endless-pagination 和其他预先推出的 django 分页模块,所有逻辑似乎都由模板标签执行,从而阻止了在渲染阶段之前执行业务逻辑的能力(这是 django 的宗旨)。

因为我的视图逻辑在模板标签执行之前遍历结果集,所以我失去了这个模块提供的优化(比如带有分页的 SQL 查询,例如限制 20;偏移量 20)。每次用户页面时,我的代码都会遍历整个未分页的结果列表,绕过模板标签提供的惰性分页优势。

没有将我的代码直接移动到分页模块中(我不想这样做,并且需要将一堆额外的数据添加到请求上下文中以在标签中使用),还有其他选择吗?

谢谢!

4

1 回答 1

5

如果您查看lazy_paginate标记,请使用LazyPaginator类来处理查询集。您可以覆盖该类以达到您的目的。为此,您需要编写 自定义模板标签。代码注释中的更多说明。

*my_app/templatetags/custom_pagination_tags.py*

from django import template
from endless_pagination.templatetags.endless import paginate
from endless_pagination.paginators import LazyPaginator

register = template.Library()

Class CustomLazyPaginator(LazyPaginator):

    def page(self, number):
        page_obj = super(CustomLazyPaginator, self).page(number)
        # page function returns page object from that you could access object_list
        object_list = page_obj.object_list
        # Do some processing here for your queryset
        # Do not remove elements otherwise you will put your self in trouble
        # Just add some values in objects as you wanted to
        page_obj.object_list = object_list # override here
        return page_obj

@register.tag
def custom_lazy_paginate(parser, token):
    return paginate(parser, token, paginator_class=CustomLazyPaginator)

现在在模板中加载您的自定义模板标签并使用它:

{% load custom_pagination_tags %}

{% custom_lazy_paginate queryset %}

困难:在 CustomLazyPaginator 类中访问请求上下文的第一种方法

是的,有一种方法可以传递请求上下文,但为了做到这一点,您需要覆盖paginate标签以及当它调用时您可以在此处render看到的方法,它不会传递任何上下文信息。以下是实现这一目标的步骤:PaginateNodepaginator_class

在中添加__init__方法CustomLazyPaginator

def __init__(self, *args, **kwargs):
    self.context = kwargs.pop('context', None)
    super(CustomLazyPaginator, self).__init__(*args, **kwargs)

复制paginate标签并将return语句从更改PaginateNode(paginator_class, objects, **kwargs)CustomPaginateNode(paginator_class, objects, **kwargs)我们将CustomPaginateNode在下面写的。

from endless_pagination.templatetags.endless import PAGINATE_EXPRESSION

@register.tag
def paginate(parser, token, paginator_class=None):
    # Validate arguments.
    try:
        tag_name, tag_args = token.contents.split(None, 1)
    except ValueError:
        msg = '%r tag requires arguments' % token.contents.split()[0]
        raise template.TemplateSyntaxError(msg)

    # Use a regexp to catch args.
    match = PAGINATE_EXPRESSION.match(tag_args)
    if match is None:
        msg = 'Invalid arguments for %r tag' % tag_name
        raise template.TemplateSyntaxError(msg)

    # Retrieve objects.
    kwargs = match.groupdict()
    objects = kwargs.pop('objects')

    # The variable name must be present if a nested context variable is passed.
    if '.' in objects and kwargs['var_name'] is None:
        msg = (
            '%(tag)r tag requires a variable name `as` argumnent if the '
            'queryset is provided as a nested context variable (%(objects)s). '
            'You must either pass a direct queryset (e.g. taking advantage '
            'of the `with` template tag) or provide a new variable name to '
            'store the resulting queryset (e.g. `%(tag)s %(objects)s as '
            'objects`).'
        ) % {'tag': tag_name, 'objects': objects}
        raise template.TemplateSyntaxError(msg)

    # Call the node.
    return CustomPaginateNode(paginator_class, objects, **kwargs)

删除我们之前调用的以下导入以避免调用原始paginate函数:

from endless_pagination.templatetags.endless import paginate

重写将上下文传递给我们的类的render方法:PaginateNodeCustomLazyPaginator

from endless_pagination.templatetags.endless import PaginateNode
from endless_pagination import (
    settings,
    utils,
)

class CustomPaginateNode(PaginateNode):
    def render(self, context):
        # Handle page number when it is not specified in querystring.
        if self.page_number_variable is None:
            default_number = self.page_number
        else:
            default_number = int(self.page_number_variable.resolve(context))

        # Calculate the number of items to show on each page.
        if self.per_page_variable is None:
            per_page = self.per_page
        else:
            per_page = int(self.per_page_variable.resolve(context))

        # Calculate the number of items to show in the first page.
        if self.first_page_variable is None:
            first_page = self.first_page or per_page
        else:
            first_page = int(self.first_page_variable.resolve(context))

        # User can override the querystring key to use in the template.
        # The default value is defined in the settings file.
        if self.querystring_key_variable is None:
            querystring_key = self.querystring_key
        else:
            querystring_key = self.querystring_key_variable.resolve(context)

        # Retrieve the override path if used.
        if self.override_path_variable is None:
            override_path = self.override_path
        else:
            override_path = self.override_path_variable.resolve(context)

        # Retrieve the queryset and create the paginator object.
        objects = self.objects.resolve(context)
        paginator = self.paginator(
            objects, per_page, first_page=first_page, orphans=settings.ORPHANS,
            context=context) # <--- passing context here

    # Normalize the default page number if a negative one is provided.
    if default_number < 0:
        default_number = utils.normalize_page_number(
            default_number, paginator.page_range)

    # The current request is used to get the requested page number.
    page_number = utils.get_page_number_from_request(
        context['request'], querystring_key, default=default_number)

    # Get the page.
    try:
        page = paginator.page(page_number)
    except EmptyPage:
        page = paginator.page(1)

    # Populate the context with required data.
    data = {
        'default_number': default_number,
        'override_path': override_path,
        'page': page,
        'querystring_key': querystring_key,
    }
    context.update({'endless': data, self.var_name: page.object_list})
    return ''

简单:在 CustomLazyPaginator 类中访问请求上下文的第二种方法

只需安装django-contrib-requestprovider并将其添加到 django 的 settings.py 中的中间件中,然后在您想要的任何位置访问当前请求:

from gadjo.requestprovider.signals import get_request

http_request = get_request()
于 2013-11-20T01:43:21.627 回答