我正在使用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">« 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 »</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表单来启用输入页面的无限滚动。
1条答案
按热度按时间3htmauhk1#
开始,你试图实现的功能(同一页面上的多个表单)在Django中本质上是坚韧的,尽管肯定是可行的。
然而,你目前使用分页器的方式是违反直觉的,你正在“反对”更标准的方式来做这件事的线索是你的视图的大小。它非常大,已经有一个很大的外部函数来保存表单。
你还写道:
我不确定数据是否在更改到下一页之前实际存储在会话中,因为每次更改页面时,表单都是空白的。
在您当前的实现中,如果不指定以下内容,此数据将不会存储到会话中:
字符串
并且在对会话进行任何更改之后必须指定此属性。
数据也不会在浏览器中持久化,原因是Django中的默认分页涉及GET Querystring URL参数;因此它不是单页的。当你从
mysite.com/objects/?page=1
转到mysite.com/objects/?page=2
时,你会得到一个完整的页面刷新,没有数据保存到数据库中(需要有效的POST请求)或浏览器(至少需要一些复杂的JS或前端框架);如果没有request.session.modified = True
参数,它也不会保存到会话中。因此,我的第一个建议是重新考虑这个实现;当然是分页,但可能是整个方法。
现在,最佳实现将取决于您的业务逻辑,但一些好的开始可能如下所示:
如果你不能做到以上两点,那也没关系,尽管这会很棘手。你仍然应该远离默认的Django分页。
这听起来像是你想要一个“单页”风格的前端感觉,数据在每个“表单示例”之间随着时间的推移而持久化。
为此,你需要自己实现大量的自定义vanilla JS,或者一些帮助库。(对于JavaScript POST请求非常有用,您可能会发现您需要实现)和**Alpine**(一个非常轻量级的前端库,有点像Vue,你可能可以用它来复制“分页”,但以一种使数据随时间持续的方式)。
然后对于每个Form,您需要在视图中小心地处理它。(即一个更新表单),然后确保你正确地传递了“示例”参数;您可以通过URL(如果你定义它包括示例id)或者通过表单本身的隐藏的“id”字段。我推荐基于URL的方法,因为这是Django处理表单的典型方式,如果你想在同一个页面上有多个表单,并且数据随着时间的推移而持久化,你可以使用HTMX来处理这些表单的更改,每个表单的数据都将通过表单示例ID的适当URL到达端点(即页面上有“多个”表单,后端逻辑可以很好地处理“单个”表单-幸运的是,没有意识到同一页面上有几个表单,POST请求异步地到达不同的端点)。