使用render_to_string时填充{%csrf_token%}?

时间:2012-04-27 16:23:04

标签: python django

我需要使用render_to_string,但我不能使用

@csrf_protect

并传递

context_instance=RequestContext(request)) 

到render_to_string,因为我认为装饰器需要一个HttpResponse,而不是一个SafeString。

如何将csrf_token放入字符串中的表单?

  

'NoneType'对象没有属性'COOKIES'

编辑:感谢您的评论,这里有一些澄清:

我正在创建一个'面板'系统,类似于'portlets',其中面板在数据库中配置(包括用于将面板呈现为字符串的python函数的名称 - 面板函数在运行时从它们导入数据库中的名称。)

为了将这些面板放到页面中,我创建了一个自定义模板标签,它从DB中读取所需面板的名称,将它们呈现为字符串,然后返回一个大的hb(作为字符串)blob正在呈现的页面。 (这允许用户选择为他们显示哪些panles。)

我的问题是其中一个面板(使用render_to_string将模板呈现为字符串的简单python函数)中有一个表单。

我需要将CSRF字段插入到该表单中,因为它将呈现为字符串。

4 个答案:

答案 0 :(得分:5)

我已经解决了这个问题,但我仍然非常喜欢一个好的解决方案。

面板功能现在看起来像:

def render_to_s(request, *args, **kwargs):
    panelDisplays = PanelDisplay.listAll()
    csrf_token_value = request.COOKIES['csrftoken']

    c = {"panelDisplays": panelDisplays, "csrf_token_value": csrf_token_value}
    return render_to_string('panels/config.html', c)

虽然模板现在包括:

<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='{{ csrf_token_value }}'/></div>

答案 1 :(得分:1)

在您的视图中,您使用request.COOKIES ['csrftoken'] 我不确定cookie是否始终可用,因此我的解决方案是在每个请求上生成csrf令牌。

from django.middleware.csrf import get_token

def render_to_s(request, *args, **kwargs):
    panelDisplays = PanelDisplay.listAll()
    csrf_token_value = get_token(self.request)

    c = {"panelDisplays": panelDisplays, "csrf_token_value": csrf_token_value}
    return render_to_string('panels/config.html', c)

答案 2 :(得分:0)

视图应始终返回HttpResponse(或其中一个祖先)。所以你应该返回HttpResponse(your_rendered_string),但它与调用render_to_response相同。如果您需要修改响应主体,也可以通过响应来实现:

response = render_to_response(...)
response.content = response.content.replace('A', 'B')
return response

答案 3 :(得分:0)

以下是完整解决方案的示例。所有这些都来自这篇优秀的帖子。

这是基于您希望在网站的多个页面上添加邀请表单而不重复自己的情况。

invitations.widgets.py:

from invitations.forms import InvitationForm
from django.middleware.csrf import get_token
from django.template.loader import render_to_string

def invitation_widget_function (request):
    # ... blah blah blah 
    # ... stuff I don't want 
    # ... to repeat in every view
    invitation_form = InvitationForm ()
    csrf_token_value = get_token(request)
    context = { 'invitation_form': invitation_form, 
                'csrf_token_value': csrf_token_value }
    return render_to_string ( 'invitation_widget_template.html', context )

invitation_widget_template.html:

<form action="/whatever/" method="post">
    {% csrf_token %}  {# don't do this because it won't work in streamed output #}
    {# do this instead: #}
    <div style='display:none'>
    <input type='hidden' name='csrfmiddlewaretoken value='{{ csrf_token_value }}'/>
    </div>
    {{ invitation_form }}
    <button type="submit">Invite Someone</button>
</form>

views.py:

from invitations.widgets import invitation_widget_function 
from django.shortcuts import render         

def page_view (request):
    invitation_widget = invitation_widget_function(request)
    context = { 'invitation_widget': invitation_widget }
    return render (request, 'page_template.html', context )

page_template.html:

<body>
    <div>
    {% include "some_normal_template.html" %} {# takes context from page_view() #}

    {{ invitation_widget }} {# not an include, but a fully rendered string #}
    {# took context from invitation_widget_function() -- has csrf token included #}
    </div>
</body>