在Django中更好的处理Ajax的方法

4
前几天我为一个我正在开发的Django应用编写了一些AJAX代码。
我来自Ruby on Rails,所以对原始JS并不熟悉。
因此,基于Rails的partials,我在某种伪代码中实现了类似以下的功能(不必担心细节):
1)使用prototype的Ajax.Updater函数('tablediv'是我想要通过Ajax更新的表格的ID,url指向正确的Django视图)
 function updateTable(){
       new Ajax.Updater('tablediv',url {params: params....etc

2) Django视图获取新数据以用于填充表格:

 def ajaxTable
     objects = Objects.object.all...
     return render_to_response('ajaxtable.html',objects)

3) ajaxtable.html只是一种类似于Rails的"partial",基本上是一个没有<table></table>标签的表格...:

   <th>{{object.data}}</th>
   <td>{{object.moredata}}</td>

所以我的实际问题是:

这个方法对我来说似乎有些不专业,我只是在搜索我想要的内容后匆忙地拼凑出来的。

这是正确的做法吗?虽然它能正常工作,但我不知道足够的知识去判断,你懂的。

4个回答

5

我认为,这取决于你想要做什么。Ajax的应用范围很广,从Google地图到简单的自动完成都有很大的复杂性和最佳方法。

然而,有一些有用的事情可以帮助你。

1)模板级别

确保在TEMPLATE_CONTEXT_PROCESSORS设置中有“django.core.context_processors.request”。然后你可以这样做:

{% if not request.is_ajax %}
<html>
  <head>
  ...
  </head>
  <body>
  ...
{% endif %}
actual content
{% if not request.is_ajax %}
</body>
</html>
{% endif %}

基本上说,如果这个页面是/test/,你可以通过浏览器请求获取完整的内容,也可以通过JavaScript请求只获取内容。有一篇博客文章更详细地解释了这一点,但我现在找不到它。

2)在视图中

在模板中,我们只需在模板中访问请求对象。在视图中,您可以做非常类似的事情。

def my_view(request):
    if requst.is_ajax():
        # handle for Ajax requests

    # otherwise handle 'normal' requests
    return HttpResponse('Hello world')

上述方法并没有与您的方法有什么不同之处,但它允许您重复使用视图并更加简洁地编写代码。我并不认为您所做的是错误或者不专业的,但您可以编写更加简洁的代码,并重复使用模板和视图。
例如,您可以只有一个模板,如果是Ajax请求,则仅返回需要更新的部分。在您的情况下,应该是表格视图。

这似乎非常聪明,使用嵌入式 Ruby 对我很有吸引力...也许太聪明了... - Joel
太聪明了吧?我真的很喜欢它。你可以快速地完全Ajax化网站的大部分内容,不幸的是每个模板只能有一个“ajax块”... 嗯,我想知道你怎么做到的。 - user67627

4

虽然有些晚了,但我想记录一下如何结合和调整由d0ugal提出的解决方案,以便更清晰地解决模板代码。

我有一个代表联系人的模型。

获取一个ContactPerson的(通用)视图如下:

def contcactperson_detail_view(request, name):
    try:
        person = ContactPerson.objects.get(slug=name)
    except:
       raise Http404
    if request.is_ajax():
        return contcactperson_detail_view_ajax(request, person)
    return list_detail.object_detail(
            request,
            queryset = ContactPerson.objects.all(),
            object_id = person.id,
            template_object_name = "contactperson",
        )

@render_to('cms/contactperson_detail_ajax.html')    
def  contcactperson_detail_view_ajax(request, person):
    return {'contactperson':person, 'is_ajax':True}

负责处理一个联系人的视图的模板称为contcactperson_detail_view.html

{% extends "index.html" %}
{% block textpane %}

<h1 id="mainheader">{{ contactperson.first_name }} {{ contactperson.family_name  }} </h1>
<div class="indentation">&nbsp;</div> 
{% include 'cms/contactperson_detail_photo.html' %}                                                                                                          
<div id="text_pane">

{% include 'cms/contactperson_detail_textpane.html' %}
</div>
{% endblock %}

这包括两个子模板

contactperson_detail_textpane.html


<p>{{ contactperson.description }}</p>
<ul>
    <li>
        <dl>
            <dt>Email</dt>
            <dd>
                {{ contactperson.mail }}
            </dd>
        </dl>
    </li>
    <li>
        <dl>
            <dt>Contact Person for</dt>
            <dd>
                <ul>
                {% for c in contactperson.categories.all %}
                    <li><a href="{% url category-view c.slug %}">{{ c }}</a></li>
                {% endfor %}
                </ul>
            </dd>
        </dl>
    </li>
</ul>

and contactperson_detail_photo.html

{% with contactperson.photo.detailphoto as pic %} 
    {% with pic.url as pic_url %}    
    <div {% if not is_ajax %}id='imageContainer'{% endif %} style="float: right;padding-right:0.5em; 
                                    padding-bottom: 1em; padding-left:0.5em;clear:both; 
                                    width:{{ pic.width }}px">   
        <div style="width:{{ pic.width}}px">                       
                <img style="clear:both" src="{{ pic_url }}" alt="{{ i.name }}"/> 
        </div>                                                                                                                    
    </div>   
    {% endwith %}
{% endwith %}

如果请求不是ajax,则使用这3个模板。

但是,如果请求是ajax,则contcactperson_detail_view将返回视图contcactperson_detail_view_ajax,该视图使用模板contactperson_detail_ajax.html进行渲染。而此模板如下所示:

<h1>{{ contactperson.first_name }} {{ contactperson.family_name  }}</h1>
{% include 'cms/contactperson_detail_photo.html' %}                                                                                                          
{% include 'cms/contactperson_detail_textpane.html' %}

因此,它使用相同的子模板,但不继承任何内容,因此只提供所需的标记。由于ajax视图将is_ajax = True传递给模板,因此可以用于调整一些小事情,例如设置正确的id属性。

无需上下文处理器或额外的url-conf。

最后是Javascript代码:

$("#contact_person_portlet a").click(function(event){
       event.preventDefault();
       $.ajax({
           type: "GET",
           url: event.target.getAttribute('href'),
           success: function(msg){
               overlay(msg);
           }
        });
    });

希望这对一些人有用。如果确实有用,请留下评论!

我相信你可以通过使用单个视图和相同的模板来实现更简单的解决方案,无论是ajax还是非ajax,但根据上下文变量继承不同的基础模板。请参见https://dev59.com/TFHTa4cB1Zd3GeqPQ2K_#4018629,这是我对该行另一个问题的回答。 - Carles Barrobés

3
无论什么情况,您至少需要两个东西:
  1. 您的javascript代码以进行调用(您已经拥有这个)

  2. 服务器端代码来处理请求(这是您的视图和url配置)

这绝对不是“hacky”的事情。
第三件事,您的模板文件是可选的-但通常是一个好习惯。您想要将标记与代码分离出来,这有很多原因。
所以我认为您有正确的想法。继续吧。

谢谢回复,我已经完成了这个项目并继续其他工作,但它一直在困扰着我(当我在忙于某些事情时,我无法停下来去论坛或Stack Overflow上提问...我没有耐心:)很高兴知道我的直觉并不完全荒谬。 - Joel

1

这个方案有什么不妥之处吗?看起来是一种完全有效的做法。

我想另一种方法是将其序列化为Json并将其发送回JavaScript 模板片段。


那是我曾经尝试过的,但当时我不像现在这样了解JavaScript,所以只需将对象作为JSON发送,将其转换为JavaScript对象文字,然后tablediv.innerHTML = foo。 - Joel
JSON是一个对象字面量,所以在JavaScript中使用它的好处是,一旦你拥有它,你就不必对它做任何事情就可以使用它。我喜欢将JSON与上面链接的模板系统(jquery,而不是prototype,抱歉)结合使用,因为你可以直接将返回的JSON传递给你设置的模板,它就会工作。 - Steerpike

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