在 Django 中是不是有一种惯用的方法来编写不显眼的 JavaScript 和/或进行 AJAX 表单提交?
Posted
技术标签:
【中文标题】在 Django 中是不是有一种惯用的方法来编写不显眼的 JavaScript 和/或进行 AJAX 表单提交?【英文标题】:Is there an idiomatic approach in Django for writing unobtrusive JavaScript and/or making AJAX form submissions?在 Django 中是否有一种惯用的方法来编写不显眼的 JavaScript 和/或进行 AJAX 表单提交? 【发布时间】:2014-05-14 14:26:24 【问题描述】:我是一名 Ruby/Rails 开发人员,现在在一家 Python/Django 商店工作。我已经开始对 Python 热身,但是,在我认为重要的某些方面,我仍然很难找到与 Rails 相媲美的 Django。我当前和未来的很多工作都将集中在向我们的 API 发出 AJAX 请求。作为一名 Rails 开发人员,我会使用不显眼的 javascript,特别是在提交表单时添加了一个 data-remote 标记,如下所示。
然后我会在控制器中编写一个方法来处理请求,并在 /assets/js 目录中的 JS 文件中使用事件委托编写一个 JavaScript/jQuery 函数来处理客户端的响应.我认为使用 Django 会有类似的方式来实现这种功能。
我想我真正想说的是,我认为 Django 会为 Rails 提供类似的“魔力”,因为每次我想发出 AJAX 请求时都不必写出 jQuery AJAX 函数。我写了一个粗略的比较(非常粗略),我将如何写出这两个。我想知道这是否是我在 Django 中的 Rails 中所做的不正确的方法。我知道 *** 不是用来发表意见的,但我认为无论你使用什么语言/框架都适用的打破原则,即通过不一遍又一遍地写出 AJAX 函数来干掉代码,并没有真正违背意见,这更像是打破公认的规则。
我目前在 Django 中处理 AJAX 请求的方法感觉不对,或者我可能只是习惯了通过 data-remote="true" 属性提供的“神奇”Rails。希望获得有关该主题的一些指导,以帮助我确定可靠的方法,谢谢。
铁路
views/some_controller/form.html.erb
<form action="<%= endpoint %>" method="post" data-remote="true" id="form">
FORM FIELDS HERE
</form>
assets/javascripts/some_model.js
$('body').on('ajax:success', '#form', function(event, data)
DO SOME STUFF HERE
);
controllers/some_controller.rb
def some_ajax_action
if request.xhr?
THIS IS AN AJAX REQUEST RENDER A VIEW PARTIAL &
MANIPULATE THE DOM WITH JS OR RESPOND WITH JSON
else
THIS ISNT AN AJAX REQUEST
end
end
DJANGO
some_app/templates/form.html
<form action="% url 'app:view' %" method="post" id="form">
FORM FIELDS HERE OR BUILD_FORM_FROM_CONTEXT
</form>
some_app/static/assets/js/some_app.js
$("#form").on("submit", function(event)
$.ajax(
type: "POST",
beforeSend: function (request)
request.setRequestHeader("X-CSRFToken", csrftoken);
,
data: data,
url: "endpoint",
dataType: 'json',
contentType: 'application/json',
).done(function(data)
cb(null, data)
).fail(function(data)
cb(data)
).always(function(data)
cb(data)
)
);
);
【问题讨论】:
“有没有一种惯用的方式来将这个 Ruby/Rails 成语表达为惯用的 Python/Django?”这是一个完全可以接受的问题。它表明您已经完成了研究。 你看过这个了吗? docs.djangoproject.com/en/dev/ref/request-response/… 嘿,杰森,我看过那些文档。这是处理调用是否为 AJAX 的好方法。但我认为我的问题更多是关于是否存在类似于 Rails 使用 UJS 的惯用方法。我找到了一个 Django-UJS 库,但它似乎没有得到维护。将data-remote="true"
添加到 html 表单或链接会通过 AJAX 发送请求。我在想也许模板过滤器可以用来做同样的事情。我会做更多的研究并找出答案。
AFAIK 大多数 Rails “魔法”来自 Rails 提供的 JavaScript 插件(在您的示例中为 rails.js
)。 Django 只专注于 Python,并没有提供太多的内置模板和开箱即用的静态文件(除了在像 admin 这样的成熟解决方案中),所以你需要以简单的方式来做。不过,如果您想自己实现 extra template tags and django.js
来封装这些内容,那么没有什么能阻止您。
@evkline:您能否就所提供的答案提供一些反馈?它将帮助我奖励赏金。如果您已经以不同的方式解决了问题,您可以写下您的解决方案作为答案。
【参考方案1】:
您的问题的答案是否定的。 Django 没有惯用的 AJAX 方法。它对 AJAX 没有意见,尤其是在前端。
由于基于类的视图 (CBV) 的结构方式,后端倾向于遵循更相似的模式;通常看到一个简单的AJAXResponseMixin
混合到 CBV 中,它利用了所有通用视图通过单一方法get_context_data
可靠地生成其上下文的事实。 mixin 可以只获取所述上下文并将其转换为 JSON。
django 没有强制使用 AJAX 模式;你只需要按照你喜欢的方式构建你自己的。
如果您喜欢 Rails 示例的方式,为什么不呢?
本练习的目的是向您展示这个想法中真正涉及的框架有多么少。只需几行代码。
首先,让我们使用您概述的语法重现您的data-remote=True
标记和事件侦听器系统。注意,这都是伪代码。
$(function()
$("[data-remote=true]").each(function(index, el)
$(el).submit(function(ev)
ev.preventDefault(); // disable default form submit. We are using ajax.
$.ajax(
url: $(this).attr('action') || '',
data: $(this).serialize(),
method: $(this).attr('method') || 'get',
success: function(response)
// on success, trigger an event with the response data
$(el).trigger('ajax:success', [response]);
,
error: function(xhr)
$(el).trigger('ajax:error', [xhr]);
)
return false;
)
)
)
完成。现在让我们在模板中使用它:
<form id="my-django-form" method="POST" data-remote="true" action="some-controller">
form.as_p
<input type="submit" />
</form>
酷,现在一些 JS 来响应这个表单的提交:
$(function()
$('body').on('ajax:success', '#my-django-form', function(event, data)
alert("My barebones ajax framework was successful!", data);
)
$('body').on('error', '#my-django-form', function(event, data)
alert("Whoops, error!");
)
)
控制器/django 视图:
def some_ajax_action(request):
""" Some basic AJAX Handler.
"""
if request.is_ajax():
return http.HttpResponse(json.dumps(
'this-is': 'some-json',
))
else:
print "This isn't an ajax request"
关于如何应用为框架和可重用模式的想法,包括许多框架共有的部分渲染。
class AJAXFrameworkMixin(object):
""" A more advanced, reusable pattern for class based views.
Perhaps also allowing partial rendering via custom header `RENDER_PARTIAL` used by the jQuery request.
"""
def dispatch(self, request, *args, **kwargs):
if request.is_ajax():
partial = request.META.get('X_RENDER_PARTIAL')
if partial:
return self.handle_ajax_partial(request, partial)
return self.handle_ajax(request)
return super(AJAXFrameworkMixin, self).dispatch(request, *args, **kwargs)
def handle_ajax(self, request):
""" Ajax specific handler.
Convert this view context into JSON.
"""
ctx = self.get_context_data()
return http.HttpResponse(json.dumps(ctx))
def handle_ajax_partial(self, request, partial):
""" Given a render partial header from the barebones AJAX framework, render said partial with context.
"""
t = template.loader.get_template(partial)
return t.render(template.RequestContext(request, self.get_context_data()))
为了完成这个示例,我们将修改初始 jQuery 脚本,以根据新的数据属性(例如 data-partial-name)设置标头。
现在我们的简单框架可以通过 HTML 数据属性在基于类的视图上调用特定方法。即设置data-partial="some-template-name.html"
将触发YourView.handle_ajax_partial
,这将返回呈现的HTML。
如果已设置data-partial
,则您可以通过向您的data-remote
函数添加默认处理程序来自动呈现/更新所述调用。
【讨论】:
【参考方案2】:这不是在 Django 中轻松实现的方法,但是通过一些额外的库,您可以更轻松地处理 ajax 表单帖子。
CsrfProtection.js - 这会将 csrf 令牌标头附加到您的 ajax 请求中。
(function()
var csrfSafeMethod, sameOrigin;
csrfSafeMethod = function(method)
return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method);
;
sameOrigin = function(url)
var host, origin, protocol, sr_origin;
host = document.location.host;
protocol = document.location.protocol;
sr_origin = '#' + host;
origin = protocol + sr_origin;
return (url === origin || url.slice(0, origin.length + 1) === origin + '/') || (url === sr_origin || url.slice(0, sr_origin.length + 1) === sr_origin + '/') || !(/^(\/\/|http:|https:).*/.test(url));
;
$.ajaxSetup(
beforeSend: function(xhr, settings)
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url))
return xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
);
).call(this);
然后包含 javascript 库 jquery.form.js。使用此库将允许您执行以下操作:
$('#form_id').ajaxSubmit(
success: function(response) console.log(response);,
error: function(response) console.log(response);
);
它可能不像您在 Ruby 中列出的那样简单,但它确实减少了 csrf 令牌和表单序列化的重复。
【讨论】:
【参考方案3】:我不相信 django 本身有一个成语。但是,有一些可重用的应用程序确实提供了非常常用的实现。
例如,Crispy 表单可以简化 django ajax 表单。无需在请求之前进行任何 csrf 处理。来自他们tutorial 的示例:
var example_form = '#example-form';
$.ajax(
url: "% url 'save_example_form' %",
type: "POST",
data: $(example_form).serialize(),
success: function(data)
if (!(data['success']))
// Here we replace the form, for the
$(example_form).replaceWith(data['form_html']);
else
// Here you can show the user a success message or do whatever you need
$(example_form).find('.success-message').show();
,
error: function ()
$(example_form).find('.error-message').show()
);
【讨论】:
以上是关于在 Django 中是不是有一种惯用的方法来编写不显眼的 JavaScript 和/或进行 AJAX 表单提交?的主要内容,如果未能解决你的问题,请参考以下文章
Django 1.8 和 Django Crispy Forms:是不是有一种简单易行的方法来实现日期选择器?
有没有一种惯用的方法来避免长的 Clojure 字符串文字?