使用Django paginator逐页显示多个Django表单,但无法保存页面中的数据

k3fezbri  于 2023-11-20  发布在  Go
关注(0)|答案(1)|浏览(95)

我正在使用Django paginator创建一个输入页面,将多个表单显示为单个页面。有了paginator,我想启用无限滚动。
我已经向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 paginator将每个表单显示为一个页面。一旦我可以保存所有表单数据到数据库,我将使用paginator表单来启用输入页面的无限滚动。

3htmauhk

3htmauhk1#

开始,你试图实现的功能(同一页面上的多个表单)在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参数,它也不会保存到会话中。
因此,我的第一个建议是重新考虑这个实现;当然是分页,但可能是整个方法。
现在,最佳实现将取决于您的业务逻辑,但一些好的开始可能如下所示:

  • 首先,有没有一种方法可以将所有小的、独立的表单 Package 成一个大的表单?如果有,你会发现这种行为变得更加易于管理。
  • 如果没有,是否有一个具有M2M关系的模型,您可以定义它来封装多表单逻辑,并且这将允许您服务于单个表单?同样,如果有,您会发现这种实现要容易得多。

如果你不能做到以上两点,那也没关系,尽管这会很棘手。你仍然应该远离默认的Django分页。
这听起来像是你想要一个“单页”风格的前端感觉,数据在每个“表单示例”之间随着时间的推移而持久化。
为此,你需要自己实现大量的自定义vanilla JS,或者一些帮助库。(对于JavaScript POST请求非常有用,您可能会发现您需要实现)和**Alpine**(一个非常轻量级的前端库,有点像Vue,你可能可以用它来复制“分页”,但以一种使数据随时间持续的方式)。
然后对于每个Form,您需要在视图中小心地处理它。(即一个更新表单),然后确保你正确地传递了“示例”参数;您可以通过URL(如果你定义它包括示例id)或者通过表单本身的隐藏的“id”字段。我推荐基于URL的方法,因为这是Django处理表单的典型方式,如果你想在同一个页面上有多个表单,并且数据随着时间的推移而持久化,你可以使用HTMX来处理这些表单的更改,每个表单的数据都将通过表单示例ID的适当URL到达端点(即页面上有“多个”表单,后端逻辑可以很好地处理“单个”表单-幸运的是,没有意识到同一页面上有几个表单,POST请求异步地到达不同的端点)。

相关问题