使用Django分页器逐页显示多个Django表单但无法保存页面数据

问题描述 投票:0回答:1

我正在使用 Django 分页器创建一个输入页面,将多个表单显示为单个页面。分页器就位后,我想启用无限滚动。

我已向 ChatGPT 提出问题,到目前为止提供的代码未保存数据。最新的解决方案甚至没有按预期显示“全部保存”按钮。我不确定在更改到下一页之前数据是否实际存储在会话中,因为每次我更改页面时,表单都是空白的。

下面是视图函数和html模板。具有正确子模板的页面的生成正在工作。

`# views.py
    from django.core.paginator import Paginator
    from django.core.exceptions import ObjectDoesNotExist
    from django.core.exceptions import ValidationError
    from django.shortcuts import render, redirect, get_object_or_404
    from django.urls import reverse
    from django.contrib.auth import authenticate, logout
    from django.contrib import messages
    from django.http import JsonResponse, HttpResponseRedirect
from django.forms.models import model_to_dict
from django.db import transaction

...

# Define a function to handle the final save operation
def save_all_forms(form_classes, form_data_list):
    # Start a database transaction
    with transaction.atomic():
        # Save the first form instance
        first_form_class = form_classes[0]
        first_form_instance = first_form_class(**form_data_list[0]).save()

        # Save subsequent form instances
        instances = [first_form_instance]
        for form_class, form_data in zip(form_classes[1:], form_data_list[1:]):
            form_instance = form_class(**form_data)
            # Assuming the foreign key to the first form is named 'page1'
            setattr(form_instance, 'page1', first_form_instance)
            form_instance.save()
            instances.append(form_instance)

        # Convert model instances to dictionaries for any further processing
        instances_dict = [model_to_dict(instance) for instance in instances]
        return instances_dict  # This list can be used for further processing if needed



def ler_new_pages_application_fraud(request):
    form_classes = [LossEventPage1Form, LossEventPage2Form, DummyForm]
    form_data_list = request.session.get('form_data_list', [{} for _ in form_classes])
    all_forms_valid = all(form_data for form_data in form_data_list)

    if request.method == 'POST':
        # This is the page of the current form to validate.
        page_number = request.GET.get('page', 1)
        try:
            page_number = int(page_number)
        except ValueError:
            page_number = 1
        page_number = max(1, min(page_number, len(form_classes)))

        # Get the form class for the current form to validate
        current_form_class = form_classes[page_number - 1]
        current_form = current_form_class(request.POST, request.FILES)

        if current_form.is_valid():
            # Store the cleaned data from the form into the session
            form_data_list[page_number - 1] = current_form.cleaned_data
            request.session['form_data_list'] = form_data_list
            all_forms_valid = all(form_data for form_data in form_data_list)

            if 'save_all' in request.POST and all_forms_valid:
                # Save all forms here
                instances = save_all_forms(form_classes, form_data_list)
                del request.session['form_data_list']  # Clear the session data after saving
                return redirect('ler_listing')  # Redirect to a success page
            elif page_number < len(form_classes):
                # Redirect to the next form page
                return redirect(f"{reverse('pages-application-fraud')}?page={page_number + 1}")

    # If not POST or forms are not valid, display current form
    page_number = request.GET.get('page', 1)
    try:
        page_number = int(page_number)
    except ValueError:
        page_number = 1
    page_number = max(1, min(page_number, len(form_classes)))
    current_form_class = form_classes[page_number - 1]
    current_form = current_form_class(initial=form_data_list[page_number - 1])

    paginator = Paginator(form_classes, 1)
    page_obj = paginator.get_page(page_number)

    context = {
        'form': current_form,
        'page_obj': page_obj,
        'all_forms_valid': all_forms_valid,
    }

    return render(request, 'ler_new_pages_application_fraud.html', context)

#html template
{% extends 'base.html' %}
{% load static %}

{% block content %}
{% if all_forms_valid %}
<form method="post">
    {% csrf_token %}
    <button name="save_all" type="submit">Save All</button>
</form>
{% else %}
<form method="post" enctype="multipart/form-data">
    {% csrf_token %}

    {% if  page_obj.number == 1 %}
    {% include 'subtemplate/lerCommonPage01p.html' %}
    {% endif %}

    {% if  page_obj.number == 2 %}
    {% include 'subtemplate/lerCommonPage02p.html' %}
    {% endif %}

    {% if  page_obj.number == 3 %}
    {% include 'subtemplate/lerCommonPage03.html' %}
    {% endif %}
</form>

{% endif %}

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
        <a href="?page=1">&laquo; first</a>
        <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">next</a>
        <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>




<script src="{% static 'js/jquery_3_5_1.min.js'%}"></script>

<!-- Add the following JavaScript code -->
<script>
    $(document).ready(function () {
        var max_length = 1500;  // Set your desired maximum character length
        var descriptionField = $('#id_incidentSummary');  // Replace 'id_description' with your field's ID

        descriptionField.on('input', function () {
            var current_length = descriptionField.val().length;
            var remaining = max_length - current_length;
            $('#char-count-incidentSummary').text(remaining + ' characters remaining');
        });
    });
</script>

{% endblock %}

#models.py
class LossEventPage1(models.Model):
    code = models.CharField(default=1, max_length=30)
    description = models.CharField(max_length=512)
    reportingEntity = models.ManyToManyField(website.models.ReportingEntity)
    id_Org = models.ForeignKey(website.models.Org, on_delete=models.SET_NULL, null=True)
    incidentSummary = models.CharField(max_length=1000)
    id_Location = models.ForeignKey(
        website.models.Location, on_delete=models.SET_NULL, null=True
    )
    locationDesc = models.CharField(max_length=4000)
    timesurvey = models.TimeField()  # Time Of Event Detection
    date_survey = models.DateField()  # Date Of Event Detection
    amount_involved = models.DecimalField(max_digits=12, decimal_places=2)
    amount_involved_estd = models.DecimalField(max_digits=12, decimal_places=2)


class LossEventPage2(models.Model):
    lossEventPage1 = models.OneToOneField(
        LossEventPage1, on_delete=models.CASCADE, null=True,
        related_name='losseventpage2'
    )
    incidentSummary = models.CharField(max_length=1000)

#forms.py
from django import forms
        
class DummyForm(forms.Form):
    pass  # No form fields required

class LossEventPage1Form(forms.ModelForm):
    date_survey = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
    timesurvey = forms.TimeField(widget=forms.TimeInput(attrs={"type": "time"}))

    class Meta:
        model = LossEventPage1
        fields = [
            "description",
            "reportingEntity",
            "id_Org",
            "timesurvey",
            "date_survey",
            "id_Location",
            "locationDesc",
            "amount_involved",
            "amount_involved_estd",
 
        ]
        widgets = {
            "reportingEntity": forms.CheckboxSelectMultiple,
        }

class LossEventPage2Form(forms.ModelForm):
    incidentSummary = forms.CharField(
        widget=forms.Textarea(attrs={"rows": 4, "cols": 50, "maxlength": 1000}),
        label="Incident Summary",
        help_text="Enter a description (max 1000 characters)",
    )

    class Meta:
        model = LossEventPage2
        fields = ["incidentSummary"]`

我已经向chatGPT寻求解决方案,但到目前为止每个解决方案都不起作用。

我正在尝试创建一个使用多个表单的输入页面,并使用 django 分页器将每个表单显示为单个页面。一旦我可以将所有表单数据保存到数据库,我将使用分页器表单来启用输入页面的无限滚动。

django django-forms infinite-scroll paginator
1个回答
0
投票

首先,您尝试实现的功能(同一页面上的多个表单)在 Django 中本质上是困难的,尽管当然是可行的。

但是,您目前使用分页器执行此操作的方式是违反直觉的,并且您“反对”更标准的方法的线索是视图的大小。它非常大,并且已经具有用于保存表格的大型外部功能。 另外,你写道:

我不确定在更改到下一页之前数据是否实际存储在会话中,因为每次我更改页面时,表单都是空白的。

在您当前的实现中,如果不指定以下内容,这些数据将不会存储到会话中:

request.session.modified = True

并且您必须在对会话进行任何更改后指定这一点。

数据也不会持久保存在浏览器中,原因是 Django 中的默认分页涉及 GET Querystring URL 参数;因此它不是单页的。当您从

mysite.com/objects/?page=1

mysite.com/objects/?page=2
时,您将获得完整的页面刷新,并且没有数据保存到数据库(需要有效的 POST 请求)或浏览器(至少需要一些复杂的 JS 或前端框架) ;如果没有
request.session.modified = True
参数,它也不会保存到会话中。
因此,我的第一个建议是重新考虑这个实施;当然是分页,但也可能是整个方法。看起来太复杂了。

现在最好的实现将取决于您的业务逻辑,但一些好的起点可能如下:

首先,有没有办法将所有小的、单独的表格包装成一个大表格?如果是这样,你会发现这种行为变得更容易管理。
  • 如果没有,您是否可以定义一个具有 M2M 关系的模型来封装多表单逻辑,并且这是否允许您提供单一表单?再说一次,如果是这样,你会发现这个实现要容易得多。
  • 如果您不能做到上述任何一项,那也没关系,尽管这会很棘手。当然,您仍然应该放弃默认的 Django 分页。

听起来您希望前端有一种“单页”风格的感觉,数据在每个“表单实例”之间随着时间的推移而持续存在。

为此,您要么需要自己实现的大量自定义普通 JS,要么需要一些辅助库。就我个人而言,我发现与 Django 完美集成的最好的两个前端库是

HTMX(对于异步 POST 请求非常有用,您可能会发现需要实现)和 Alpine(一个非常轻量级的前端) lib,有点像 Vue,您可以使用它来复制“分页”,但以一种使数据随着时间的推移而持久存在的方式)。 然后对于每个表单,您都需要在您的视图中仔细处理它。如果它是一个已经存在的对象的表单(即更新表单),那么请确保您正确地向它传递了“实例”参数;您可以通过 URL(如果您将其定义为包含实例 id)或通过表单本身中的隐藏“id”字段来执行此操作。我会推荐基于 URL 的方法,因为它是 Django 处理表单的典型方式,如果您希望在同一页面上有多个表单,并且数据随着时间的推移持续存在,您可以使用 HTMX 来处理对这些表单的更改,每个表单的数据都通过表单实例 ID 的适当 URL 发送到端点(即,页面上有“多个”表单,后端逻辑对于“单个”表单运行良好 - 幸福地没有意识到页面上有多个表单)同一页面,POST 请求异步发送到不同的端点)。

© www.soinside.com 2019 - 2024. All rights reserved.