Вопрос или проблема
Мне любопытно, есть ли способ иметь несколько различных форм в одном представлении и нажимать одну кнопку отправки, чтобы информация была сохранена. У меня есть следующие формы, первая из которых — это моя общая форма, которая должна быть создана первой:
class GeneralForm(forms.ModelForm):
name = forms.CharField(max_length=50)
TYPE_CHOICES = (
("C","C"),
("E","E")
)
STATUS_CHOICES = (
("A", "A"),
("F", "F"),
)
type=forms.ChoiceField(choices=STATUS_CHOICES)
number=forms.CharField(max_length=50)
TURN_CHOICES = (
("1", "1"),
("2", "2"),
("3", "3")
)
turn = forms.ChoiceField(choices=TURN_CHOICES)
class Meta:
model = models.General
fields=["name","type","number","turn"]
Вторая форма нуждается в экземпляре первой, а третья форма тоже:
class TypeOneForm(forms.ModelForm):
num_chairs=forms.IntegerField()
num_instalations = forms.IntegerField()
total = forms.IntegerField()
num_programs = forms.IntegerField()
class Meta:
model = models.TypeOne
fields=["num_chairs","num_instalations","total","billetes_cien"]
class TypeTwoForm(forms.ModelForm):
num_available_seats=forms.IntegerField()
num_available_instalations = forms.IntegerField()
total = forms.IntegerField()
num_new_programs = forms.IntegerField()
class Meta:
model = models.TypeTwo
fields=["num_available_seats", "num_available_instalations","total","num_programs" ]
Это мои модели:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
# Создайте ваши модели здесь.
class General(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=100)
TYPE_CHOICES = {
"C": "C",
"E": "E",
}
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
STATUS_CHOICES = {
"A": "A",
"F": "F",
}
status = models.CharField(max_length=10, choices=STATUS_CHOICES)
number = models.CharField(max_length=100)
TURN_CHOICES = {
"1": "1",
"2": "2",
"3": "3",
}
turn = models.CharField(max_length=10, choices=TURN_CHOICES)
def __str__(self):
return self.name
class One(models.Model):
one = models.OneToOneField(General, on_delete=models.CASCADE, primary_key=True)
num_chairs = models.IntegerField(default=0)
num_installations = models.IntegerField(default=0)
total = models.IntegerField(default=0)
num_programs = models.IntegerField(default=0)
class Two(models.Model):
two = models.OneToOneField(General, on_delete=models.CASCADE, primary_key=True)
num_available_seats = models.IntegerField(default=0)
num_available_installations = models.IntegerField(default=0)
total = models.IntegerField(default=0)
num_new_programs = models.IntegerField(default=0)
Это мои представления, но я также не уверен, как написать представление для создания и обновления для этого специфического случая.
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
)
from .models import General, One,Two
# Создайте ваши представления здесь.
class GeneralListView(ListView):
model = General
template_name = "testing/user_testing.html"
context_object_name="generals"
class GeneralDetailView(DetailView):
model = General
class GeneralCreateView(LoginRequiredMixin, CreateView):
model = General
fields="__all__"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class GeneralUpdateView(LoginRequiredMixin, UpdateView):
pass
Я читал, что я мог бы использовать FormSets, но я не уверен, могу ли я использовать разные формы вместо нескольких экземпляров одной и той же формы.
Инструмент, который вам нужен здесь, — это аргумент prefix для классов форм. Вам потребуется переопределить некоторые методы представления, такие как get_form и, возможно, get_form_class, потому что классы на основе представлений не предназначены для обработки более одного типа формы по умолчанию. Вам также может потребоваться переопределить метод post в представлениях, чтобы загрузить данные из POST-запроса в формы.
Аргумент prefix означает, что данные формы, отправленные в ваше приложение django, будут “неймспейсированы”, так что класс формы будет загружать только данные POST, которые ему нужны из запроса.
В зависимости от того, как вы рендерите свои формы, вам может понадобиться убедиться, что префикс также отображается в атрибуте name HTML-входа.
Это будет больше работы, чтобы переопределить весь код представления на основе модели (согласно моему опыту), чтобы все это соединить: вероятно, используйте более общий класс FormView, но вам нужно будет воспроизвести большую часть той же логики CreateView и UpdateView, чтобы выполнить обработку и сохранение (например, вызвать form.save() в методе form_valid класса FormView)
Мне всегда нравился CCBV для распутывания порядка вызова методов представлений на основе классов.
Еще одна заметка: вы всегда можете рендерить более одной формы на странице, но сделать так, чтобы отправления попали в разные представления. Если формы зависят друг от друга, было бы проще написать одну форму, которая будет обрабатывать вставку cleaned_data в экземпляры модели и их сохранение. То есть, в зависимости от сценария использования и опыта, который вы хотите предоставить своим пользователям, вам нужно будет принять решение о том, как обрабатывать форму.
.
Ответ или решение
Обработка нескольких форм в одном представлении Django
Вопрос о том, как обработать несколько различных форм в одном представлении с единой кнопкой отправки, актуален для многих разработчиков на Django. Рассмотрим, как это можно реализовать, основываясь на вашем примере с классами форм и моделями.
Основные понятия
-
Формы и модели: Ваша задача состоит в создании нескольких форм (
GeneralForm
,TypeOneForm
,TypeTwoForm
) и моделей (General
,One
,Two
), которые взаимосвязаны. Первая форма (GeneralForm
) служит основой для второй и третьей форм. -
Представление: Мы используем
FormView
для обработки этих форм, так как она предоставляет большую гибкость по сравнению сCreateView
иUpdateView
, когда речь идет о нескольких формах.
Шаги реализации
1. Изменение представления
В вашем представлении вам нужно создать обработчик, который будет уметь работать сразу с несколькими формами. Используя FormView
, мы переопределим методы для управления формами.
from django.views import View
from django.shortcuts import render, redirect
from .forms import GeneralForm, TypeOneForm, TypeTwoForm
from .models import General, One, Two
class MultiFormView(View):
def get(self, request, *args, **kwargs):
general_form = GeneralForm()
type_one_form = TypeOneForm(prefix='type_one')
type_two_form = TypeTwoForm(prefix='type_two')
return render(request, 'your_template.html', {
'general_form': general_form,
'type_one_form': type_one_form,
'type_two_form': type_two_form,
})
def post(self, request, *args, **kwargs):
general_form = GeneralForm(request.POST)
type_one_form = TypeOneForm(request.POST, prefix='type_one')
type_two_form = TypeTwoForm(request.POST, prefix='type_two')
if general_form.is_valid() and type_one_form.is_valid() and type_two_form.is_valid():
# Сохраняем общие данные
general_instance = general_form.save(commit=False)
general_instance.author = request.user
general_instance.save()
# Сохраняем данные TypeOne
type_one_instance = type_one_form.save(commit=False)
type_one_instance.one = general_instance
type_one_instance.save()
# Сохраняем данные TypeTwo
type_two_instance = type_two_form.save(commit=False)
type_two_instance.two = general_instance
type_two_instance.save()
return redirect('some_view_name') # Укажите редирект
return render(request, 'your_template.html', {
'general_form': general_form,
'type_one_form': type_one_form,
'type_two_form': type_two_form,
})
2. Настройка HTML-шаблона
Ваша форма будет включать все три формы. Чтобы избежать конфликтов имен, используйте префиксы для всех форм, кроме общей формы.
<form method="post">
{% csrf_token %}
{{ general_form.as_p }}
<h3>Type One Form</h3>
{{ type_one_form.as_p }} <!-- здесь будет использоваться префикс 'type_one' -->
<h3>Type Two Form</h3>
{{ type_two_form.as_p }} <!-- здесь будет использоваться префикс 'type_two' -->
<button type="submit">Submit</button>
</form>
3. Обработка ошибок валидации
Не забудьте добавить обработку ошибок валидации, чтобы пользователи могли видеть, что пошло не так, если данные были введены некорректно.
Заключение
С помощью переопределения базового метода представления post
и использования префиксов для форм можно успешно обрабатывать несколько форм в одном представлении Django. Данный подход позволяет сохранять данные в зависимости от других и обеспечивает более чистое и понятное решение задачи.
Для дальнейшего чтения рекомендую обратиться к документации Django о работе с формами и классовым представлениям. Это поможет углубить ваши знания и навыки в разработке на Django.