如何通过 Ajax 调用更新数据表

Posted

技术标签:

【中文标题】如何通过 Ajax 调用更新数据表【英文标题】:How to update a Datatable via Ajax call 【发布时间】:2021-12-31 20:22:23 【问题描述】:

每当有人访问 URL /project_page 时,我都有一个表格显示模型中的内容。

在该页面上,用户可以添加文件,我希望表格能够实时更新,而无需不断刷新。

为此,我尝试实现一个每隔几秒更新一次表格内容的 Ajax 函数。因为这是几年前提出的建议here

我认为该功能已实现,并且我在 Ajax 成功功能中正确收到了data,但我不知道如何将其“注入”到表中。

我也想知道是否有更优化或pythonic的方式来实现这个结果。

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 部分)

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 部分)

<table id="ittFileUploadTable" class="display nowrap" >
    <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"   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>                            

【问题讨论】:

尝试更新你的标签这个问题与javascript或jquery更相关 按照建议添加了 JS ;) project_page url 名称是什么?因为在 form.is_valid() 内部,最好重定向用户而不是呈现当前页面的 html。 path('project_page', views.project_page, name='project_page'), 不要使用jQuery Ajax,Datatables 自带的Ajax 功能更适合这个任务。 datatables.net/manual/ajax 从您的服务器返回 JSON 数据并配置您希望您的表在 DataTables 选项中的外观。 【参考方案1】:

这个问题与 javascript 更相关。但这是我的想法,你可以如何使用 partial_html 解决这个问题。这只是一个想法,你可以如何做到这一点,但也许一个 javascript 的人会给出一个漂亮的答案)))。 视图.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" >
    <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"   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(只是一个简单的 html,不要放任何 body 或 head 标签,仅此创建该文件)

<table id="ittFileUploadTable" class="display nowrap" >
    <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"   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
        
    );

【讨论】:

感谢您的回答,我明白了!但我不认为隐藏和显示是一个好主意,在这种情况下,即使不到 1 毫秒,用户也不会注意到表格“消失”。此外,我无法创建新的 HTML 文件。我认为这个问题与 Ajax 的 Datatables API 比 JS 本身更相关,但是 IDK 如何连接它:datatables.net/reference/api/ajax.reload() @Cheknov .hide() 默认持续时间是 400ms 我不认为用户会注意到它。6 个月前我遇到了同样的问题,我正在构建一个聊天应用程序,我需要这样的东西显示消息历史记录并且效果很好用户甚至没有注意到它,但是在数据库性能之后我将其更改为django-channels【参考方案2】:

解决方案很大程度上取决于 JSON 响应是什么。

HTML

如果您的响应是要显示的实际 HTML 块,看起来有点像这样:

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

那么你可以这样:

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

JSON

如果您的数据是 JSON,那么创建一个模板函数会很棒,就像这样:

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"   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>
    `;

像这样给你的tbody 补水:

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

小提琴

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"   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()">

【讨论】:

【参考方案3】:

我找到了解决办法!

正如@mark_b 所建议的,我使用了数据表 Ajax 而不是 jQuery AJAX

现在可以了,这是我的代码:

$('#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"   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'
);

【讨论】:

以上是关于如何通过 Ajax 调用更新数据表的主要内容,如果未能解决你的问题,请参考以下文章

通过 Jquery 使用 Ajax 调用函数/数据库更新

Ajax 调用只更新一次

手动加载更新数据表数据而不调用ajax

通过ajax调用更新行后使用jQuery对表进行排序

单击按钮时,如何触发 jquery datatables fnServerData 通过 AJAX 更新表?

如何使用Ajax动态更新JSON-LD脚本?