【问题标题】:Page with a dynamic number of forms, each form for each table row页面具有动态数量的表格,每个表格对应每个表格行
【发布时间】:2022-01-19 02:23:32
【问题描述】:

公司有工人在一天中进行各种活动。每个活动都有start_timefinish_time。工作人员通常忘记发出活动结束的哔哔声(finish_time),这是因为有一个存储过程read_unended time_from time_to 读取time_fromtime_to 之间的记录,而finish_time 没有finish_time(是NULL )。 例如

id  name    day         start_time              finish_time place   activity
38  Thomas  2021-12-03  2021-12-03 08:51:38.000 NULL    p1  a1
28  Charles 2021-12-02  2021-12-02 12:29:03.000 NULL    p2  a2
49  John    2021-12-06  2021-12-06 11:59:48.000 NULL    p3  a3
68  Jessie  2021-12-08  2021-12-08 10:55:12.000 NULL    p4  a4
82  Susanne 2021-12-10  2021-12-10 12:38:03.000 NULL    p5  a5

(forms.py)中有一个表格

class FromToForm(Form):
    start_date = DateField(widget=AdminDateWidget())
    start_time = TimeField(widget=AdminTimeWidget())
    end_date = DateField(widget=AdminDateWidget())
    end_time = TimeField(widget=AdminTimeWidget())

(views.py) 中有一个视图显示了这样的表格。

def ending(req):
    from_to_form = FromToForm()
    result = []
    context = {
                'form': from_to_form,
                'result': result
              }
    if req.method == "POST":
        from_to_form = FromToForm(req.POST)
        if from_to_form.is_valid():
            start = datetime.combine(from_to_form.cleaned_data['start_date'], from_to_form.cleaned_data['start_time']).isoformat()
            end = datetime.combine(from_to_form.cleaned_data['end_date'], from_to_form.cleaned_data['end_time']).isoformat()
            with connections["mssql_database"].cursor() as cursor:
                cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" % (start, end))
                result = cursor.fetchall()
            context['result'] = result
            return render(req, 'ending.html', context)
        else:
            return render(req, 'ending.html', context)
    else:
        return render(req, 'ending.html', context)

以及templates.py 中的关联模板。

<form action='.' method='POST'>{% csrf_token %}
                {{ form.media }}
                {{ form.as_p }}
                <input type='submit' value='Read unended' class="btn btn-secondary" />
        </form>
{% if result %}
        <table class="table mb-0">
                <thead>
                        <tr>
                                <th>id</th>
                                <th>name</th>
                                <th>day</th>
                                <th>start_time</th>
                                <th>finish_time</th>
                                <th>place</th>
                                <th>activity</th>
                        </tr>
                </thead>
                <tbody>
                   {%for i in result %}
                     <tr>
                           <td>{{i.0}}</td>
                           <td>{{i.1}}</td>
                           <td>{{i.2}}</td>
                           <td>{{i.3}}</td>
                           <td>CELL TO INSERT END TIME*</td>
                           <td>{{i.5}}</td>
                           <td>{{i.6}}</td>
                           <td>BUTTON TO FINISH THIS ACTIVITY**<td/>
                     </tr>
                   {% endfor %}
                </tbody>
        </table>
        {% else %}
           Every activity is ended
        {% endif %}

** 和 * 尚未实现。 我想实现以下功能。在动态生成的表格的每一行中,都应该有一个按钮** 来完成这个活动(这一行),时间是应用程序的用户插入的时间。在那一刻,页面应该刷新并且该行不应该再显示了,因为这个活动已经分配了finish_time。如何实现这样的视图和模板?我需要在现有表单中添加动态生成的字段吗?你有什么建议?

【问题讨论】:

  • 安全警报:cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" % (start, end)) 允许 sql 注入攻击。改用这个cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" ,(start, end))

标签: django ajax django-views django-forms


【解决方案1】:

您必须创建一个函数来发布您的数据并创建另一个函数来获取表体

模板.html

     <table class="table mb-0">
    <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>day</th>
            <th>start_time</th>
            <th>finish_time</th>
            <th>place</th>
            <th>activity</th>
        </tr>
    </thead>
    <tbody id="tbodyId">
        {% for i in result %}
        <tr>
            <td id="tdId_{{ i.0 }}0">{{i.0}}</td>
            <td id="tdId_{{ i.0 }}1">{{i.1}}</td>
            <td id="tdId_{{ i.0 }}2">{{i.2}}</td>
            <td id="tdId_{{ i.0 }}3">{{i.3}}</td>
            <td><input type="date" id="tdId_{{i.0}}4"/></td>
            <td id="tdId_{{ i.0 }}5">{{i.5}}</td>
            <td id="tdId_{{ i.0 }}6">{{i.6}}</td>
            <td><button value="{{i.0}}" type="button" onclick="setEndActivity(this.value)">End</button><td/>
        </tr>
        {% endfor %}
    </tbody>
</table>
<script>
function loadResults(activityId) {
    let xhttpRequest = new XMLHttpRequest()
    const start_date = document.getElementById(`tdId_${activityId}2`).value
    const start_time = document.getElementById(`tdId_${activityId}3`).value
    const end_date = document.getElementById(`tdId_${activityId}2`).value
    const end_time = document.getElementById(`tdId_${activityId}4`).value
    xhttpRequest.onreadystatechange = function (data) {
        if (this.readyState === 4 && this.status === 200) {
            document.getElementById("tbodyId").innerHTML = this.response
        }
    }
    xhttpRequest.open("GET", `./ajax/load-results?start_date=${start_date}&end_date=${end_date}&start_time=${start_time}&end_time=${end_time}`, true)
    xhttpRequest.send()
}

function setEndActivity(activityId) {
    const dataToBackEnd = new FormData()
    dataToBackEnd.append("activity", activityId)
    dataToBackEnd.append("start_date", document.getElementById(`tdId_${activityId}2`).value)
    dataToBackEnd.append("start_time", document.getElementById(`tdId_${activityId}3`).value)
    dataToBackEnd.append("end_date", document.getElementById(`tdId_${activityId}2`).value)
    dataToBackEnd.append("end_time", document.getElementById(`tdId_${activityId}4`).value)
    const request = new Request('./ajax/end-activity/',
    {
        method: 'POST',
        headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value },
        body: dataToBackEnd
    })
    fetch(request, {
        method: 'POST',
        mode: 'same-origin'
    }).then(
        function(response) {
            if (response.status === 200) {
                loadResults(activityId)
            } else {
                alert("Error")
            }
        }
    )
}
</script>

在您的视图中,您将添加 2 个视图,如果您修改“结束”视图以接收帖子并返回 http 响应,则仅添加 1 个视图

views.py

def ajax_ending(req):
    if req.method == "POST":
        from_to_form = FromToForm(req.POST)
        if from_to_form.is_valid():
            # save in database
            return HttpResponse(status=200)
        else:
            return HttpResponse(status=400)
    else:
        return HttpResponse(status=405)


def ajax_load_results(request):
if request.method == "GET":
    if request.user:
        # get the activities
        start_date = request.GET['start_date']
        start_time = request.GET['start_time']
        start_date_time = f'{start_date} {start_time}'
        end_date = request.GET['end_date']
        end_time = request.GET['end_time']
        end_date_time = f'{end_date} {end_time}'
        # this datetime.strptime depends of how you write your date and time
        start = datetime.strptime(start_date_time, "%d-%m-%Y %H:%M:%S").isoformat()
        end = datetime.strptime(end_date_time, "%d-%m-%Y %H:%M:%S").isoformat()
        # i don't understand why you are executing queries like this, so i assume they work
        with connections["mssql_database"].cursor() as cursor:
            cursor.execute("EXEC read_unended @dt_od='%s', @dt_do='%s'" % (start, end))
            result = cursor.fetchall()
        context['result'] = result
        return render(request, 'ajax-ending.html', {"context": context})
    else:
        return HttpResponse(status=403)
else:
    return HttpResponse(status=405)

您将有另一个 template.html 仅用于表格的 tbody

ajax-ending.html

{% for i in result %}
    <tr>
        <td id="tdId_{{ i.0 }}0">{{i.0}}</td>
        <td id="tdId_{{ i.0 }}1">{{i.1}}</td>
        <td id="tdId_{{ i.0 }}2">{{i.2}}</td>
        <td id="tdId_{{ i.0 }}3">{{i.3}}</td>
        <td id="tdId_{{ i.0 }}4">CELL TO INSERT END TIME*</td>
        <td id="tdId_{{ i.0 }}5">{{i.5}}</td>
        <td id="tdId_{{ i.0 }}6">{{i.6}}</td>
        <td><button value="{{i.0}}" type="button" onclick="setEndActivity(this.value)">End</button><td/>
    </tr>
{% endfor %}

ulrs.py

from django.urls import path
from . import views


app_name = 'your_app_name'

urlpatterns = [
        'ending/',
        views.ending, 
        name='ending'),
    path(
        'ending/ajax/end-activity/',
        views.ajax_ending, 
        name='ajax_ending'),
    path(
        'ending/ajax/load-results/',
        views.ajax_load_results, 
        name='ajax_load_results'),
]

(未测试,如有错误请告知)

【讨论】:

  • 谢谢。对不起,我对 Ajax 完全陌生。您能否澄清一下这个xhttpRequest.open("GET", "./ajax/load-results/", true) 和这个const request = new Request('./ajax/end-activity/', 以及urls.py 中的对应条目会是什么样子?以及ajax-ending.html 会是什么样子?我可能应该在base.html 中插入一个链接来注册 ajax?
  • 哦,这只是我在项目中命名 url 的方式,您不需要编写 ajax 部分,但我认为这有助于组织。 ajax-ending就是上面这段代码,只是部分的拷贝,这个ajax-ending.html可以和template.html在同一个目录下。我将编辑我的 anwser 以添加一个 urls.py 示例
  • 为什么这里总是2 的值,对吗? dataToBackEnd.append("start_date", document.getElementById(tdId_${i.id}2).value) dataToBackEnd.append("start_time", document.getElementById(tdId_${i.id}2).value) dataToBackEnd.append("end_date", document.getElementById(tdId_${i.id}2).value) dataToBackEnd.append("end_time", document.getElementById(tdId_${i.id}2).value)
  • 哦不,我错了,这是表信息的 td 的 id,我编辑了但我没有看到 "end_date" td 所以我让与 start_date 相同
  • @Luiz 现在,这很有意义。我相信我会设法完成它。
猜你喜欢
  • 1970-01-01
  • 2014-03-11
  • 1970-01-01
  • 1970-01-01
  • 2014-05-25
  • 2017-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多