Несколько форм, которые отличаются в представлении Django

Вопрос или проблема

Мне любопытно, есть ли способ иметь несколько различных форм в одном представлении и нажимать одну кнопку отправки, чтобы информация была сохранена. У меня есть следующие формы, первая из которых — это моя общая форма, которая должна быть создана первой:

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. Рассмотрим, как это можно реализовать, основываясь на вашем примере с классами форм и моделями.

Основные понятия

  1. Формы и модели: Ваша задача состоит в создании нескольких форм (GeneralForm, TypeOneForm, TypeTwoForm) и моделей (General, One, Two), которые взаимосвязаны. Первая форма (GeneralForm) служит основой для второй и третьей форм.

  2. Представление: Мы используем 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.

Оцените материал
Добавить комментарий

Капча загружается...