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
相关产品推荐
相关产品推荐

