skip to Main Content

I have a table that displays the content from a model whenever someone accesses the URL /project_page.

On that page, the user can add files and I would like the table to be updated in real-time without having to constantly refresh.

For that purpose, I have tried to implement an Ajax function that updates the table content every few seconds. Since it is something that was suggested a few years ago here

I think the function is implemented and I receive the data properly in the Ajax success function but I don’t know how to ‘inject it’ to the table.

enter image description here

I would also like to know if there is a more optimal or pythonic way to achieve this result.

urls.py

path('project_page_ajax/', views.project_page_ajax, name='project_page_ajax'),

views.py

@login_required
def project_page(request):
    
    context = {}
    context['nbar'] = 'projects'
    if request.method == 'POST':
        print(request.FILES)
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            file_hist = form.save(commit=False)
            file_hist.user = request.user
            # file is saved
            file_hist.save()
            file_hist_results = FileHistory.objects.all().filter(user=request.user)
            context['file_hist_results'] = file_hist_results
            print(type(context['file_hist_results']))
            return render(request, 'project_page.html', context)
        print (form.errors)
    else:
        form = UploadFileForm()
    file_hist_results = FileHistory.objects.all().filter(user=request.user)
    context['file_hist_results'] = file_hist_results
    context['form'] = form
    return render(request, 'project_page.html', context)

@login_required
def project_page_ajax(request):
    response = dict()
    if request.method == 'GET':
        file_hist_results = FileHistory.objects.all().filter(user=request.user).values()
        #response.update({'file_hist_results': file_hist_results})
        return JsonResponse({"file_hist_results": list(file_hist_results)})
    return HttpResponse('')

project_page.html (JS PART)

var intervalID = setInterval(updateTable, 10000);

function updateTable()
{
    $.ajax({
        method: "GET",
        url: "/project_page_ajax/",
        success: function(data, textStatus, request) {
            console.log(data); 
        }
    });
}

project_page.html(HTML PART)

<table id="ittFileUploadTable" class="display nowrap" width="100%">
    <thead>
        <tr class="ittLineItemsTh">
        <th style="text-align:center;">File Name</th>
        <th style="text-align:center;">Submitted</th>
        <th style="text-align:center;">Updated</th>
        <th style="text-align:center;">User</th>
        <th style="text-align:center;">Action</th>
        </tr>
    </thead>
    <tbody>
        {% for histfiles in file_hist_results %}
        <tr>
        <td>{{ histfiles.filename }}</td>
        <td>{{ histfiles.uploaded }}</td>
        <td>{{ histfiles.updated }}</td>
        <td>{{ histfiles.user }}</td>
        <td>
        <button id="delete-itt-file" type="button" class="btn btn-secondary">
        <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
        <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"></path>
        </svg>
        </button>
        </td>
        </tr>
        {% endfor %}
    </tbody>
</table>                            

3

Answers


  1. Chosen as BEST ANSWER

    I found a solution!

    As @mark_b sugested, I used the datatables Ajax instead of jQuery AJAX

    Now it works, here is my code:

    $('#ittFileUploadTable').DataTable( {
        responsive: true,
        autowidth: false,
        destroy: true,
        deferRender: true,
        ajax: {
            url: '/project_page_ajax/',
            type: 'GET',
            data: {},
            dataSrc: ""
        },
        columns: [
            {"data": "fields.filename"},
            {"data": "fields.uploaded"},
            {"data": "fields.updated"},
            {"data": "fields.user"},
            {"data": "pk"},
        ],
        columnDefs: [
            { className: 'text-center', targets: [1] },
            {
                targets: [0],
                class: 'text-center',
                orderable: false,
                render: function (data, type, row) {
                    var buttons = '<a href="/media/'+data+'" target="_blank">'+data+'</a>';
                    return buttons;
                }
            },
            {
                targets: [-1],
                class: 'text-center',
                orderable: false,
                render: function (data, type, row) {
                    var buttons = '<form method="post" action="delete_file/'+data+'/"><button type="submit" class="btn btn-danger"><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="white" class="bi bi-trash-fill" viewBox="0 0 16 16"><path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"/></svg></button></form>';
                    return buttons;
                }
            },
        ],
        order: [
            [0, 'asc']
        ],
        "pagingType": "numbers",
        dom: 'rt'
    });
    

  2. this question is more related to javascript.but here is my idea how you can solve this with partial_html.This is just an idea how you can do it but maybe a javascript guy will come with a beautifull answer ))).
    views.py

    from django.shortcuts import redirect 
    @login_required
    def project_page(request):
        
        context = {}
        context['nbar'] = 'projects'
        if request.method == 'POST':
            print(request.FILES)
            form = UploadFileForm(request.POST, request.FILES)
            if form.is_valid():
                file_hist = form.save(commit=False)
                file_hist.user = request.user
                # file is saved
                file_hist.save()
                return redirect('project_page')
            print (form.errors)
        else:
            form = UploadFileForm()
        file_hist_results = FileHistory.objects.all().filter(user=request.user)
        context['file_hist_results'] = file_hist_results
        context['form'] = form
        return render(request, 'project_page.html', context)
    
    @login_required
    def project_page_ajax(request):
        response = dict()
        if request.method == 'GET':
            file_hist_results = FileHistory.objects.all().filter(user=request.user).values()
            return render(request, 'partial_page.html', {'file_hist_results':file_hist_results})
    

    project_page.html

    <table id="ittFileUploadTable" class="display nowrap" width="100%">
        <thead>
            <tr class="ittLineItemsTh">
            <th style="text-align:center;">File Name</th>
            <th style="text-align:center;">Submitted</th>
            <th style="text-align:center;">Updated</th>
            <th style="text-align:center;">User</th>
            <th style="text-align:center;">Action</th>
            </tr>
        </thead>
        <tbody>
            {% for histfiles in file_hist_results %}
            <tr>
            <td>{{ histfiles.filename }}</td>
            <td>{{ histfiles.uploaded }}</td>
            <td>{{ histfiles.updated }}</td>
            <td>{{ histfiles.user }}</td>
            <td>
            <button id="delete-itt-file" type="button" class="btn btn-secondary">
            <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
            <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"></path>
            </svg>
            </button>
            </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    

    partial_page.html(just a simple html do not put any body or head tag just this create that file with only this)

    <table id="ittFileUploadTable" class="display nowrap" width="100%">
        <thead>
            <tr class="ittLineItemsTh">
            <th style="text-align:center;">File Name</th>
            <th style="text-align:center;">Submitted</th>
            <th style="text-align:center;">Updated</th>
            <th style="text-align:center;">User</th>
            <th style="text-align:center;">Action</th>
            </tr>
        </thead>
        <tbody>
            {% for histfiles in file_hist_results %}
            <tr>
            <td>{{ histfiles.filename }}</td>
            <td>{{ histfiles.uploaded }}</td>
            <td>{{ histfiles.updated }}</td>
            <td>{{ histfiles.user }}</td>
            <td>
            <button id="delete-itt-file" type="button" class="btn btn-secondary">
            <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
            <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"></path>
            </svg>
            </button>
            </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    

    js

    var intervalID = setInterval(updateTable, 10000);
    
    function updateTable()
    {
        $.ajax({
            method: "GET",
            url: "/project_page_ajax/",
            success: function(response) {
                $("#ittFileUploadTable").hide(); // first hide the current table
                $("#ittFileUploadTable").append(response); // and append the data
            }
        });
    }
    
    Login or Signup to reply.
  3. The solution largely depends on what the JSON response is.

    HTML

    If your response is the actual HTML chunk to be displayed, looking somewhat like this:

    <tr><!-- Some tds --></tr>
    <tr><!-- Some tds --></tr>
    <tr><!-- Some tds --></tr>
    <!-- ... -->
    <tr><!-- Some tds --></tr>
    

    Then you can do it like this:

    document.querySelector("#ittFileUploadTable tbody").innerHTML = yourTemplate;
    

    JSON

    If your data is JSON, then it would be great to create a template function, like this one:

    function fileUploadRecordTemplate(record) {
        return  `
            <td>record.filename</td>
            <td>record.uploaded</td>
            <td>record.updated</td>
            <td>record.user</td>
            <td>
                <button id="delete-itt-file" type="button" class="btn btn-secondary">
                    <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
            <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"></path>
                    </svg>
                </button>
            </td>
        `;
    }
    

    And hydrate your tbody like this:

    let output = [];
    for (let record of data) output.push(fileUploadRecordTemplate(record));
    document.querySelector("#ittFileUploadTable tbody").innerHTML = yourTemplate;
    

    Fiddle

    let index = 1;
    
    //t1
    
    function updateT1() {
        //We generate some HTML, but if you get an HTML from server-side, then you can
        //skip this part
        let template = "";
        let rows = parseInt(document.getElementById("t1rows").value);
        for (let i = 0; i < rows; i++) {
            template += `
                <tr>
                    <td>${index++}</td>
                    <td>${index++}</td>
                    <td>${index++}</td>
                    <td>${index++}</td>
                </tr>
            `;
        }
        //This is the actual operation which refreshes the HTML
        document.querySelector("#t1 > tbody").innerHTML = template;
    }
    
    //t2
    function fileUploadRecordTemplate(record) {
        return  `
           <tr>
            <td>${record.filename}</td>
            <td>${record.uploaded}</td>
            <td>${record.updated}</td>
            <td>${record.user}</td>
            <td>
                <button id="delete-itt-file" type="button" class="btn btn-secondary">
                    <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
            <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"></path>
                    </svg>
                </button>
            </td>
           </tr>
        `;
    }
    
    function updateT2() {
        let rows = parseInt(document.getElementById("t2rows").value);
        let data = [];
        for (let i = 0; i < rows; i++) {
            data.push({
                filename: index++,
                uploaded: index++,
                updated: index++,
                user: index++
            });
        }
        
        let output = [];
        for (let record of data) output.push(fileUploadRecordTemplate(record));
        document.querySelector("#t2 tbody").innerHTML = output.join("");
    }
    <table id="t1">
        <thead>
            <th>First</th>
            <th>Second</th>
            <th>Third</th>
            <th>Fourth</th>
        </thead>
        <tbody>
        </tbody>
    </table>
    
    Number of rows: <input type="number" value="4" id="t1rows"><input type="button" value="OK" onclick="updateT1()">
    
    <table id="t2">
        <thead>
            <th>First</th>
            <th>Second</th>
            <th>Third</th>
            <th>Fourth</th>
            <th>Fifth</th>
        </thead>
        <tbody>
        </tbody>
    </table>
    
    Number of rows: <input type="number" value="4" id="t2rows"><input type="button" value="OK" onclick="updateT2()">
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search