You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django单视图多不同表单提交实现及增改视图技术问询

在Django单个视图中集成多个不同表单并一键提交存储

需求说明

需要在单个视图里集成三个不同表单,点击一次提交按钮完成所有数据存储,其中GeneralForm必须优先创建,另外两个表单(TypeOneForm、TypeTwoForm)依赖General模型的实例。

现有表单代码

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

# Create your models here.
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
# Create your views here.

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适用于同一表单的多个实例,你的场景是不同表单,直接在视图中手动处理多个表单更合适。

1. 修改CreateView(创建数据)

重写get和post方法,将三个表单加入上下文,提交时优先验证GeneralForm,创建实例后关联另外两个表单并验证保存。

from django.shortcuts import redirect
from .forms import GeneralForm, TypeOneForm, TypeTwoForm

class GeneralCreateView(LoginRequiredMixin, CreateView):
    template_name = 'testing/general_create.html'  # 自定义模板路径

    def get(self, request, *args, **kwargs):
        # 初始化三个空表单
        general_form = GeneralForm()
        type_one_form = TypeOneForm()
        type_two_form = TypeTwoForm()
        return render(request, self.template_name, {
            '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)
        type_two_form = TypeTwoForm(request.POST)

        # 优先验证General表单
        if general_form.is_valid():
            # 创建并保存General实例(先不提交到数据库)
            general_instance = general_form.save(commit=False)
            general_instance.author = request.user
            general_instance.save()

            # 验证并保存TypeOne表单,关联已创建的General实例
            if type_one_form.is_valid():
                type_one_instance = type_one_form.save(commit=False)
                type_one_instance.one = general_instance  # 关联One模型的外键
                type_one_instance.save()

            # 验证并保存TypeTwo表单,关联已创建的General实例
            if type_two_form.is_valid():
                type_two_instance = type_two_form.save(commit=False)
                type_two_instance.two = general_instance  # 关联Two模型的外键
                type_two_instance.save()

            # 保存成功后跳转到详情页
            return redirect('general-detail', pk=general_instance.pk)
        
        # 验证失败,返回带错误的表单
        return render(request, self.template_name, {
            'general_form': general_form,
            'type_one_form': type_one_form,
            'type_two_form': type_two_form
        })

2. 修改UpdateView(更新数据)

更新时先获取已有General实例,关联对应的One和Two实例,初始化表单时传入现有数据,提交时依次验证更新。

class GeneralUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = General
    template_name = 'testing/general_update.html'

    def get(self, request, *args, **kwargs):
        general_instance = self.get_object()
        # 初始化General表单,传入现有实例数据
        general_form = GeneralForm(instance=general_instance)
        
        # 处理TypeOne表单:存在关联实例则加载数据,否则为空表单
        try:
            one_instance = general_instance.one
            type_one_form = TypeOneForm(instance=one_instance)
        except One.DoesNotExist:
            type_one_form = TypeOneForm()
        
        # 处理TypeTwo表单:存在关联实例则加载数据,否则为空表单
        try:
            two_instance = general_instance.two
            type_two_form = TypeTwoForm(instance=two_instance)
        except Two.DoesNotExist:
            type_two_form = TypeTwoForm()

        return render(request, self.template_name, {
            'general_form': general_form,
            'type_one_form': type_one_form,
            'type_two_form': type_two_form,
            'general': general_instance
        })

    def post(self, request, *args, **kwargs):
        general_instance = self.get_object()
        general_form = GeneralForm(request.POST, instance=general_instance)
        
        # 处理TypeOne表单的更新/创建
        try:
            one_instance = general_instance.one
            type_one_form = TypeOneForm(request.POST, instance=one_instance)
        except One.DoesNotExist:
            type_one_form = TypeOneForm(request.POST)
        
        # 处理TypeTwo表单的更新/创建
        try:
            two_instance = general_instance.two
            type_two_form = TypeTwoForm(request.POST, instance=two_instance)
        except Two.DoesNotExist:
            type_two_form = TypeTwoForm(request.POST)

        # 验证所有表单
        if general_form.is_valid() and type_one_form.is_valid() and type_two_form.is_valid():
            # 更新General实例
            general_form.save()
            
            # 更新或创建TypeOne实例
            if hasattr(general_instance, 'one'):
                type_one_form.save()
            else:
                one_instance = type_one_form.save(commit=False)
                one_instance.one = general_instance
                one_instance.save()
            
            # 更新或创建TypeTwo实例
            if hasattr(general_instance, 'two'):
                type_two_form.save()
            else:
                two_instance = type_two_form.save(commit=False)
                two_instance.two = general_instance
                two_instance.save()

            return redirect('general-detail', pk=general_instance.pk)
        
        # 验证失败返回带错误的表单
        return render(request, self.template_name, {
            'general_form': general_form,
            'type_one_form': type_one_form,
            'type_two_form': type_two_form,
            'general': general_instance
        })

    # 验证当前用户是否为General实例的作者
    def test_func(self):
        general = self.get_object()
        return self.request.user == general.author

3. 模板示例(创建页面)

将三个表单放在同一个<form>标签内,使用同一个提交按钮:

<form method="post">
    {% csrf_token %}
    <h3>基础信息</h3>
    {{ general_form.as_p }}

    <h3>类型一信息</h3>
    {{ type_one_form.as_p }}

    <h3>类型二信息</h3>
    {{ type_two_form.as_p }}

    <button type="submit">提交所有数据</button>
</form>

关键注意事项

  • 表单字段需与模型严格匹配:比如TypeOneForm的Meta字段billetes_cien不存在于One模型,需改为num_programs;TypeTwoForm的num_programs需改为模型中的num_new_programs。
  • 所有表单必须在同一个<form>标签中,确保提交时所有字段数据都能发送到视图。

内容的提问来源于stack exchange,提问作者Perla De la O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:59:50