Django - 为什么我应该使用render_to_response方法?

25

考虑一下:

return render(request, 'index.html', {..context..})
return render_to_response('index.html', {..context..})

一方面,render更加干净和pythonic。另一方面,你使用request作为你的第一个参数,我觉得多余且令人困惑。所以我开始思考更大的差异......

根据文档

render()与使用context_instance参数强制使用RequestContext的render_to_response()调用相同。

因此,区别仅在于使用RequestContext。那么RequestContext的重要性是什么?让我们再次查看文档

特殊的Context类[...]与普通的django.template.Context略有不同。第一个区别是它将HttpRequest作为其第一个参数。

好的。这几乎完全没有关系

第二个区别是它根据您的TEMPLATE_CONTEXT_PROCESSORS设置自动填充上下文与一些变量[...]除了这些之外,RequestContext始终使用django.core.context_processors.csrf[...] 它是故意硬编码的,并且不能通过TEMPLATE_CONTEXT_PROCESSORS设置关闭。

因此,这是重要的部分-确保所有上下文处理器正常工作,特别是csrf。因此,回到我的第一个例子,这些实际上是相同的:

return render(request, 'index.html', {...})
return render_to_response('index.html', {...}, context_instance=RequestContext(request))

现在,第二个例子明显更糟糕,整个东西看起来异常复杂。所以我的大问题是为什么还要使用render_to_response?为什么不将其弃用?

其他我想到的问题:

  1. 没有更好的方式来强制使用RequestContext作为默认值吗?
  2. 有没有办法避免将request作为参数传递?这太冗长了。我发现一篇博客文章展示了如何将render_to_response变成一个易于使用的装饰器。我们不能用类似的方法来处理render吗?
  3. 是否对此问题(如果它确实是一个问题)有任何想法?我在未来弃用时间表中看不到任何相关内容。考虑到render是在django 1.3中开发出来的,特别是为了解决render_to_response的问题,并且大家都同意不应该使用render_to_response,这一点尤其令人困惑。

我知道这似乎有些偏离主题,但我希望能得到解释为什么render_to_response还在使用和/或在何种情况下使用render_to_response会比render更合适(如果有的话)的例子。

2个回答

33
大多数应用程序使用render_to_response,因为自Django 1.3以来一直是默认推荐的选项。两者并存的原因是历史性的,废弃render_to_response将会迫使许多代码被重写,在小版本中这样做是不礼貌的。然而在这个django开发者线程中,他们说可以将其包含在2.0版的弃用时间表中。
以下是Russell Keith-Magee的引用,他是Django核心开发人员之一。Keith-Magee回答了Jacob Kaplan-Moss发布的一个问题,他是另一位Django贡献者,提出了废弃render_to_response的问题:
我认为我们应该弃用render_to_response(),转而使用render()。render_to_response()只是render(request=None, ...),对吧?保留两者的原因除了废弃会带来的代码变动之外没有什么特别的原因。Keith-Magee回答说:我在2.0计划中没有问题废弃它,但在接下来的18个月/2个版本中迁移每个render_to_response()的使用似乎是一种极端的措施,不对整个用户群体强制执行更新render_to_response()并不需要任何实际努力。虽然没人讨论过这个废弃问题,但我猜你的问题的答案是:没有技术原因,只是他们不想在小版本中强制更新整个代码库。

这个回答比另一个好多了。如果您允许,我计划在将其标记为正确之前进一步研究该主题并编辑您的答案以包括引用和其他内容。这个链接有来自Django核心开发人员之一的答案。谢谢您。 - yuvi

8

太长不看

当应用上下文处理器时

当您使用RequestContext时,您直接提供的变量首先被添加,然后是由上下文处理器提供的任何变量。这意味着上下文处理器可能会覆盖您提供的变量,因此请注意避免与上下文处理器提供的变量重叠的变量名称。


首先让我们看一下方法render_to_responserender的定义。

def render_to_response(*args, **kwargs):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    httpresponse_kwargs = {'content_type': kwargs.pop('content_type', None)}

    return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)

def render(request, *args, **kwargs):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    Uses a RequestContext by default.
    """
    httpresponse_kwargs = {
        'content_type': kwargs.pop('content_type', None),
        'status': kwargs.pop('status', None),
    }

    if 'context_instance' in kwargs:
        context_instance = kwargs.pop('context_instance')
        if kwargs.get('current_app', None):
            raise ValueError('If you provide a context_instance you must '
                             'set its current_app before calling render()')
    else:
        current_app = kwargs.pop('current_app', None)
        context_instance = RequestContext(request, current_app=current_app)

    kwargs['context_instance'] = context_instance

    return HttpResponse(loader.render_to_string(*args, **kwargs),
                        **httpresponse_kwargs)

有没有更好的方法来强制使用RequestContext作为默认值?

注意子类化上下文:RequestContext部分中

如果您使用Django的render_to_response()快捷方式将字典内容填充到模板中,则默认情况下会传递Context实例而不是RequestContext

从上面的代码中,方法 render_to_response 调用了方法 loader.render_to_string,其中在 这一行 检查了 context_instance 参数。
方法 render_to_string 的代码清单。
def render_to_string(template_name, dictionary=None, context_instance=None,
                     dirs=None):
    """
    Loads the given template_name and renders it with the given dictionary as
    context. The template_name may be a string to load a single template using
    get_template, or it may be a tuple to use select_template to find one of
    the templates in the list. Returns a string.
    """
    dictionary = dictionary or {}
    if isinstance(template_name, (list, tuple)):
        t = select_template(template_name, dirs)
    else:
        t = get_template(template_name, dirs)
    if not context_instance:
        return t.render(Context(dictionary))
    # Add the dictionary to the context stack, ensuring it gets removed again
    # to keep the context_instance in the same state it started in.
    with context_instance.push(dictionary):
        return t.render(context_instance)

我们能否用render编写简单易用的装饰器呢?

我们可以编写这个装饰器,但是你的问题是主观的。很难说它是否易于使用。这在很大程度上取决于具体情况。


有没有一种避免将请求作为参数传递的方法?

render()与调用render_to_response()并使用强制使用RequestContext参数的context_instance参数相同。

class RequestContext此行中定义。 class RequestContext的代码清单。

class RequestContext(Context):
    """
    This subclass of template.Context automatically populates itself using
    the processors defined in TEMPLATE_CONTEXT_PROCESSORS.
    Additional processors can be specified as a list of callables
    using the "processors" keyword argument.
    """
    def __init__(self, request, dict_=None, processors=None, current_app=None,
            use_l10n=None, use_tz=None):
        Context.__init__(self, dict_, current_app=current_app,
                use_l10n=use_l10n, use_tz=use_tz)
        if processors is None:
            processors = ()
        else:
            processors = tuple(processors)
        updates = dict()
        for processor in get_standard_processors() + processors:
            updates.update(processor(request))
        self.update(updates)

上一个问题不需要回答,如果你理解Django背后的代码是如何工作的。

1
虽然这篇文章有所拓展,但我并不认为它是一个答案(至少现在还不是)。你只是重复了解释并向我展示源代码,而这些都是我自己已经阅读和研究过的。在这里没有任何东西能够回答我关于“为什么render_to_response仍然存在”的重要问题(什么?为了使用上下文处理器保留的名称?开玩笑吧,这根本不是一个理由)。我知道RequestContext需要实际请求作为其初始化的一部分,但我仍然不完全相信没有办法避免将其用作参数。 - yuvi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接