【问题标题】:Why does Django 1.7 tell me that the csrf_token was not provided in the context after sending a successful ajax request?为什么 Django 1.7 在发送成功的 ajax 请求后告诉我上下文中没有提供 csrf_token?
【发布时间】:2015-07-07 06:08:50
【问题描述】:

当我在我的 Django 1.7 应用程序中获取模板并呈现表单时,我在 Python 服务器控制台上没有收到任何错误。我可以使用 Django 文档中提供的 X-CSRFHeader 建议使用 AJAX 提交表单,并且请求成功并且不会抛出 403 禁止。我什至可以使用不同的信息(这是所需的行为)再次提交此表单并且仍然成功。

我担心的是,在第一个 POST 请求之后,控制台显示:

UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.'

第一次渲染模板时,它使用render(request, 'appname/index.html', context) 渲染它,据我了解,它应该包含RenderContext() 以更有效的方法提供的相同信息。

有人知道可能出了什么问题吗?

我的 ajax 代码:

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
  // test that a given url is a same-origin URL
  // url could be relative or scheme relative or absolute
  var host = document.location.host; // host + port
  var protocol = document.location.protocol;
  var sr_origin = '//' + host;
  var origin = protocol + sr_origin;
  // Allow absolute or scheme relative URLs to same origin
  return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
      (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
      // or any other URL that isn't scheme relative or absolute i.e relative.
      !(/^(\/\/|http:|https:).*/.test(url));
}
$(function(){
var csrftoken = $.cookie('csrftoken');
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

// Define ajax submit function
$('.tab-pane form').submit(function (event){
  $.ajax({
    url: $(this).attr('action'),
    type: $(this).attr('method'),
    data: new FormData($('.tab-pane.active form').get(0)),
    dataType: "json",
    processData: false,
    contentType: false,
    success: function(data) {
      // Re-enable submit button
      $(".submit-btn").removeClass("disabled");
      $(".cancel-btn").removeClass("disabled");

      // Show errors or clean and reset form
      $('.tab-pane.active form div:first').replaceWith(data['form_html']);

      if (data['success']) {
        // Close modal
        $('#incidentForm').modal('hide');

        // Add point to map
        var point = geojsonMarker(JSON.parse(data['point']), data['point_type'])
        point.bindPopup(getPopup(point.getLayers()[0]));
        incidentData.addLayer(point);

        // Pan to point
        map.setView(point.getBounds().getCenter(), 18, {'animate': true});
        setTimeout(function(){
          point.openPopup();
        }, 300);

        // Print success or failure message
        var message = "<strong>" + '{% trans "Thank you!" %}' + "</strong><br>" + '{% trans "Your incident marker was successfully added." %}';
        showMessage(message);
      }
    }
  });
  event.preventDefault();
});
})

我的查看方法如下:

@require_POST
def postTheft(request):
     return postPoint(request, TheftForm)

def postPoint(request, Form):
    """Submit a user point submission to the database. Normalize geometry and activate push notifications."""
    form = Form(request.POST)
    form.data = form.data.copy()

# Convert coords to valid geometry
try:
    form.data['geom'] = normalizeGeometry(form.data['geom'])
except(ValueError):
    messages.error(request, '<strong>' + _('Error') + '</strong><br>' + _('No point was selected for this type of report.'))

# Validate and submit to db
if form.is_valid():
    point = form.save()
    # Errors with push notifications should not affect reporting
    if not settings.DEBUG:
        try: pushNotification.pushNotification(point)
        except: pass

    return JsonResponse({
        'success': True,
        'point': GeoJSONSerializer().serialize([point,]),
        'point_type': point.p_type,
        'form_html': render_crispy_form(Form())
    })
else:
    logger.debug("Form not valid")

# Else: error occurred
form.data['geom'] = form.data['geom'].json
form_html = render_crispy_form(form)
return JsonResponse({'success': False, 'form_html': form_html})

呈现初始模板的代码:

def index(request, lat=None, lng=None, zoom=None):
incidents = Incident.objects.select_related('point').all()

context = {
    # Model data used by map
    'collisions': incidents.filter(p_type__exact="collision"),
    'nearmisses': incidents.filter(p_type__exact="nearmiss"),
    'hazards': Hazard.objects.select_related('point').all(),
    'thefts': Theft.objects.select_related('point').all(),
    "geofences": AlertArea.objects.filter(user=request.user.id),

    # Form data used by map
    "incidentForm": IncidentForm(),
    "geofenceForm": GeofenceForm(),
    "hazardForm": HazardForm(),
    "theftForm": TheftForm(),

    "editForm": EditForm()
}

# Add zoom and center data if present
if not None in [lat, lng, zoom]:
    context['lat']= float(lat)
    context['lng']= float(lng)
    context['zoom']= int(zoom)

return render(request, 'mapApp/index.html', context)

【问题讨论】:

  • render_crispy_form 方法是什么?如果它使用 csrf 令牌呈现表单,那么我希望它将 request 作为参数。
  • 您似乎没有首先显示呈现模板的代码。
  • render_crispy_form 是一种将 django 模型呈现为漂亮的引导 html 的方法。它自动包含一个{% csrf_token %} 标签,但可以禁用它。我已禁用它,因为我在所有帖子请求中都使用了 X-CSRFHeader 标头。
  • 编辑问题以包含我的初始模板渲染

标签: ajax django csrf django-1.7 django-csrf


【解决方案1】:

在我的 django crispy_forms 模板中禁用 csrf_tag 时我搞砸了。因此,csrf 令牌可用作标头,但 render_crispy_form 试图渲染 csrf 令牌,即使它在从 JsonResponse 传回时不存在。

Alasdair 的评论为我指明了正确的方向。 感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 2021-01-31
    相关资源
    最近更新 更多