Update DOM without reloading the page in Django

2020-06-16 05:35发布

I have two picklists in DOM, first is populated during loading of URL when a method in views loads the page. Second picklist's content depends on what user selects in first picklist. How do I bind python method to event when user makes a selection in first picklist, make some manipulations in python, return a list of vallues I'd like to be displayed in the second picklist and then populate it without refreshing the page?

My template looks like this:

{% block content %}

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

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

Next condition is waiting for first_selection argument which should be returned to template after user selects a value in first picklist.

{% 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 %}

My views.py method looks like this:

def events(request):
try:
    list_of_events = Event.objects.all()
except:
    raise Http404('Something\'s wrong with events page loader')
return render(request, 'web_service/schedule.html', {
    'list_of_events': list_of_events

})

标签: python django
3条回答
够拽才男人
2楼-- · 2020-06-16 06:19

You can render all the possible second picklist with a display = none then you can add a javascript like:

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 try here

查看更多
趁早两清
3楼-- · 2020-06-16 06:35

I had a similar issue to OP (the accepted answer is the closest I can find to what I came up), and this was the top hit to my google search, so I'll share what I came up with.

The main difference is I set the dataType to 'html' and just appended the rendered template directly to the element. (if you didnt change the dataType you'd end up with exceptions--wouldn't work in the manner I've implemented)

//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

Note: I'm not sure if this is considered best practice

Note: only tested on Django 2.2 and Python 3.6.x

查看更多
仙女界的扛把子
4楼-- · 2020-06-16 06:37

In Django, at least now, there's no direct way to dynamically call python method from html template without refreshing the page.

To call python method and see it's effect in template without refreshing the page you'll need a bit of JS, dedicated url pattern and a view. It’s not as simple as calling instance method, but not as hard as it may seem.

The example below is a demonstration how to respond to a button click, send some data to a view and return the result to template without refreshing DOM.


The only way to call python code from template is relate to in the same way as we relate to url, this means we have to create new url pattern. Then call necessary view and return response to template as JsonResponse.

Note: make sure to import jquery at the inside bottom of your <body> tag.

First of all we need to create responder which will handle button click and create AJAX request to url connected to view. AJAX request passes input as parameter to our url pattern, meaning this parameter will be passed to django view. If something returns from our call to view then data is being unpacked in success closure.

Let’s say our template looks like this:

<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 handling clicks and request looks like this:

<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>

New pattern is needed in urls.py:

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

Note: the ajax/ part of url and path of url pattern itself has no influence on how this call is handled. It can be anything you want, for example: ^foo/bar/$.

Last step is adding responding Django view. This simple example returns the input with some additional text, but generally this is the place where you can call other python methods and do whatever you want:

def answer_me(request):
    user_input = request.GET.get('inputValue')
    data = {'response': f'You typed: {user_input}'}
    return JsonResponse(data)
查看更多
登录 后发表回答