【问题标题】:Update DOM without reloading the page in Django更新 DOM 而无需在 Django 中重新加载页面
【发布时间】:2017-08-27 16:04:39
【问题描述】:

我在 DOM 中有两个选项列表,第一个填充了对视图的初始调用(即加载页面)。第二个选项列表的内容取决于用户在第一个选项中选择的内容。

假设选择列表是:

  1. list_of_events
  2. list_of_rooms

每次第一个选项列表的状态发生变化(用户选择不同的事件)时,我都需要在不刷新页面的情况下更新第二个选项列表(可用房间)的内容。这可能吗?

我的模板如下所示:

{% block content %}

{% if list_of_events %}
    <form>
        <select>
            {% for event in list_of_events %}
                <option value="name">{{ event.title }}</option>
            {% endfor %}
        </select>
    </form>

{% else %}
    <p>No events available now.</p>
{% endif %}

目前,第二个选项列表 list_of_rooms 仅在用户从 list_of_events 选项列表中选择任何内容时才可见。如果用户在list_of_events 中选择一个值,则first_selection 参数将传递给模板。

{% if first_selection %}
    <form>
        <select>
            {% for room in list_of_rooms %}
                <option value="name">{{ room.id }}</option>
            {% endfor %}
        </select>
    </form>
{% endif %}

<input type="submit" value="Submit">

{% endblock %}

我的views.py 函数如下所示:

def events(request):
    list_of_events = Event.objects.all()
    return render(
        request, 
        'web_service/schedule.html', {'list_of_events': list_of_events}
    )

【问题讨论】:

  • 你考虑过 Ajax 吗?
  • 您只能重新填充页面而不使用 Javascript 重新加载它。它不能在 Python 中完成,尽管您可以在 Python 中返回所需的数据。有很多关于 AJAX 的教程。
  • 不幸的是我不熟悉它,但我确实有 Angular4 的经验,所以我正在寻找一些相似之处。可以简单地将 typescript 方法绑定到 onClick 事件,并且 DOM 将自动更新而无需刷新页面。
  • Typescript 只是带有一些额外花里胡哨的 Javascript。基本上情况相同。
  • 如果你只是需要根据一个事件渲染不同的内容,任何Javascript框架都可以做到。

标签: python django


【解决方案1】:

至少在 Django 中,没有直接的方法可以在不刷新页面的情况下从html 模板动态调用python 方法。

要调用python 方法并在模板中查看它的效果而不刷新页面,您需要一点JS、专用url 模式和view。它不像调用实例方法那么简单,但也没有看起来那么难。

以下示例演示如何响应按钮click,向view 发送一些数据并将结果返回到模板而不刷新DOM。


从模板调用python代码的唯一方法是与url相关,这意味着我们必须创建新的url pattern。然后调用必要的view 并将响应返回到模板为JsonResponse。

注意:确保在&lt;body&gt; 标签的内底部导入jquery。

首先我们需要创建响应者来处理按钮点击并创建AJAX请求连接到view的url。 AJAX request 将 input 作为参数传递给我们的 url 模式,这意味着该参数将传递给 django view。如果我们对 view 的调用返回了某些内容,则数据将在 success 闭包中解包。

假设我们的模板如下所示:

<input type="text" id="user-input" autofocus=""><br>
<button type="button" id="sender">Send data</button><br>
<p id="p-text">foo bar</p>

处理点击和请求的脚本如下所示:

<script>

$("#sender").click(function () {
    var input = $('#user-input').val();

    $.ajax({
        url: '{% url 'get_response' %}',
        data: {
          'inputValue': input
        },
        dataType: 'json',
        success: function (data) {
          document.getElementById('p-text').innerHTML = data["response"];
        }
      });
    });

</script>

urls.py 中需要新模式:

urlpatterns = [
    ...
    url(r'^ajax/get_response/$', views.answer_me, name='get_response')
    ...
]

注意: url 的ajax/ 部分和url 模式本身的路径对如何处理此调用没有影响。它可以是任何你想要的,例如:^foo/bar/$。

最后一步是添加响应的 Django view。这个简单的示例返回带有一些附加文本的输入,但通常在这里你可以调用其他 python 方法并做任何你想做的事情:

def answer_me(request):
    user_input = request.GET.get('inputValue')
    data = {'response': f'You typed: {user_input}'}
    return JsonResponse(data)

【讨论】:

    【解决方案2】:

    您可以使用 display = none 呈现所有可能的第二个选项列表,然后您可以添加类似的 javascript:

    function show(select_item) {
            if (select_item == "apple") {
                hiddenDiv.style.visibility='visible';
                hiddenDiv.style.display='block';
                Form.fileURL.focus();
            } 
            else{
                hiddenDiv.style.visibility='hidden';
                hiddenDiv.style.display='none';
            }
        }   
    </script>  
    
    <form>
    <label>Fruit:</label>
        <select name="Fruit" onchange="java_script_:show(this.options[this.selectedIndex].value)">
            <option value='pear'>pear</option>
            <option value='apple'>apple</option>
            <option value='grapes'>grapes</option>
        </select>
    
    <div id='hiddenDiv' style="display: none;">
        <label>Are you sure you want to eat the apple?</label>
        <select name="AppleQuestion">
            <option value='Yes'>Yes</option>
            <option value='No'>No</option>
        </select>
    </div>
    </form>
    

    https://codepen.io/metrafull2/pen/WEgWLo 试试这里

    【讨论】:

    • 此方案与调用python方法无关,与模型无关
    • 当然是这样,我说渲染所有可能的选项并根据显示选项,比使用 angular 或 ajax 更容易做到这一点,你没有发布你的视图和你的模板,所以很难理解你在做什么
    • 您在第二个选项列表的十几个选项范围内绝对正确,但是对于第一个选项列表中的每个选项,我有几百个第二选项列表的子选项,我需要发送第一个和第二个值无论如何选择列表回到模型,所以我可能别无选择,只能使用 ajax 方式。但是感谢您的想法,我可能有一天会使用它。
    • 好吧,我以为你没有太多选择,无论如何,如果你喜欢请投票
    • 随便发表你的观点和你的模板,我可以帮你
    【解决方案3】:

    我遇到了与 OP 类似的问题(接受的答案是我能找到的最接近我想出的答案),这是我的谷歌搜索的热门答案,所以我将分享我的想法。

    主要区别是我将dataType 设置为“html”,并且只是将渲染的模板直接附加到元素上。 (如果您没有更改dataType,您最终会遇到异常——不会按照我实施的方式工作)

    //parent_template.html
    {% load static %}
    <script src="{% static "jquery.js" %}"></script>
    
    {% block first %}
    <div class="row">
        {% for x in my_list %}
        <button class="some-filter" id="{{x}}"></button>
        {% endfor %}
    </div>
    {% endblock first %}
    
    {% block second %}
    <div class="row" id="some_id"></div>
    {% endblock second %}
    
    <script>
         $(".some-filter").on({
           click: e=> {
               var name = $( e.target )[0].id;
               $.ajax({
                 url: '/ajax/info_getter/',
                 data: {
                   'name': name,
                 },
                 dataType: 'html',
                 success: function (data) {
                   if (data) {
                     $("#some_id").html(data);
                     $(".some-filter").removeClass('selected');
                     $( e.target ).addClass('selected');
                   }
                 }
               });
           }
       })
    </script>
    
    
    
    // child_template.html
    // my big ass template containing lots of image files
    {% load static %}
    ...other stuff
    
    // urls.py
    from django.urls import path
    from . import views
    urlpatterns = [
        ...
        path('ajax/info_getter/', views.info_getter, name='info_getter'),
        ...
    ]
    
    
    
    // views.py
    from django.shortcuts import render
    ...
    def info_getter(request):
        name = request.GET.get('name', None)
        if name:
            context["name"] = name
    
        response = render(request, "child_template.html", context=context)
        return response
    

    注意:我不确定这是否被认为是最佳做法

    注意:仅在 Django 2.2 和 Python 3.6.x 上测试

    【讨论】:

      猜你喜欢
      • 2019-07-10
      • 2018-11-15
      • 2016-01-12
      • 1970-01-01
      • 1970-01-01
      • 2016-02-01
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多