简体   繁体   English

在使用django endless-pagination渲染之前,如何进行结果后处理?

[英]How to do result post-processing before rendering with django endless-pagination?

I am trying to figure out if it's possible to do view post-processing on my queryset before rendering it in a django template that uses django-endless-pagination for infinite scroll. 我试图弄清楚是否有可能在将django-endless-pagination用于无限滚动的django模板中呈现查询集之前对其进行查询后处理。

I have view-specific logic that omits certain results from the queryset based on context, as well as adding attributes to the objects in the list for use by the templates. 我有特定于视图的逻辑,该逻辑根据上下文忽略了来自查询集的某些结果,以及将属性添加到列表中的对象以供模板使用。 This logic cannot be executed via SQL as it is not inherent to the model. 该逻辑不能通过SQL执行,因为它不是模型固有的。 It must be done in python. 必须在python中完成。

With django-endless-pagination and other pre-rolled django pagination modules, all the logic seems to be executed by templatetags, thus preventing the ability to do business logic before the rendering stage (which is a django tenet). 使用django-endless-pagination和其他预滚动的django分页模块,所有逻辑似乎都由templatetags执行,从而阻止了在呈现阶段之前进行业务逻辑的能力(这是django的宗旨)。

Because my view logic runs through the result set before the template tags execute, I'm losing the optimizations offered by this module (like SQL queries with paging eg limit 20; offset 20). 因为我的视图逻辑是在执行模板标签之前遍历结果集的,所以我失去了此模块提供的优化(例如带有分页的SQL查询,例如限制20;偏移量20)。 My code traverses the entire unpaged result list every time the user pages, bypassing the lazy pagination benefits offered by the template tag. 每次用户分页时,我的代码都会遍历整个未分页的结果列表,从而绕过了template标签所提供的惰性分页优势。

Short of moving my code into the pagination module directly (which I'd rather not do and would require adding a bunch of extra data into the request context for use in a tag), is there any alternative? 没有将我的代码直接移到分页模块中(我宁愿不这样做,并且需要将大量额外数据添加到请求上下文中以用于标记中),还有其他选择吗?

Thanks! 谢谢!

If you look lazy_paginate tag use LazyPaginator class to process the queryset. 如果您看lazy_paginate标记,请使用LazyPaginator类来处理查询集。 You can override that class to serve your purpose. 您可以重写该类以满足您的目的。 In order to do that you need to write Custom Template Tag . 为此,您需要编写Custom Template Tag More instructions in the code comments. 代码注释中的更多说明。

*my_app/templatetags/custom_pagination_tags.py* * 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)

Now in template load your custom template tags and use that instead: 现在在模板中加载您的自定义模板标签,并使用它:

{% load custom_pagination_tags %}

{% custom_lazy_paginate queryset %}

Difficult: First Approach To Access Request Context In CustomLazyPaginator Class 困难:CustomLazyPaginator类中访问请求上下文的第一种方法

Yes there is a way to pass the request context, but in order to do that you need to override paginate tag and also the render method of PaginateNode as you can see here when it calls the paginator_class it does not pass any context information. 是的,有一种方法可以传递请求上下文,但是要做到这一点,您需要覆盖paginate标签以及PaginateNoderender方法,正如您在此处调用paginator_class时可以在此处看到的那样,它不传递任何上下文信息。 Below are the steps to achieve that: 以下是实现该目标的步骤:

Add __init__ method in CustomLazyPaginator : CustomLazyPaginator添加__init__方法:

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

Copy the paginate tag and change the return statement from PaginateNode(paginator_class, objects, **kwargs) to CustomPaginateNode(paginator_class, objects, **kwargs) we will write CustomPaginateNode below. 复制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)

Remove the following import which we call earlier to avoid calling original paginate function: 删除以下我们先前调用的导入,以避免调用原始paginate函数:

from endless_pagination.templatetags.endless import paginate

Override the render method of PaginateNode to pass context to our CustomLazyPaginator class: 重写PaginateNoderender方法,以将上下文传递给我们的CustomLazyPaginator类:

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 ''

Simple: Second Approach To Access Request Context In CustomLazyPaginator Class 简单:访问CustomLazyPaginator类中的请求上下文的第二种方法

Just install django-contrib-requestprovider and add it in middleware in django's settings.py and access current request any where you want as: 只需安装django-contrib-requestprovider并将其添加到django的settings.py的中间件中,然后以任何您想要的位置访问当前请求即可:

from gadjo.requestprovider.signals import get_request

http_request = get_request()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM