每天40分玩转Django:Django表单集

devtools/2024/12/28 1:41:45/

Django表单集

一、今日学习内容概述

学习模块重要程度主要内容
表单集基础⭐⭐⭐⭐⭐表单集定义、基本用法
内联表单集⭐⭐⭐⭐⭐内联表单、关联数据
表单集验证⭐⭐⭐⭐自定义验证、错误处理
动态表单集⭐⭐⭐⭐动态添加删除表单

二、基本模型定义

python"># models.py
from django.db import modelsclass Author(models.Model):name = models.CharField('姓名', max_length=100)email = models.EmailField('邮箱')bio = models.TextField('简介', blank=True)def __str__(self):return self.nameclass Book(models.Model):title = models.CharField('书名', max_length=200)author = models.ForeignKey(Author,on_delete=models.CASCADE,related_name='books')isbn = models.CharField('ISBN', max_length=13)published_date = models.DateField('出版日期')price = models.DecimalField('价格', max_digits=10, decimal_places=2)def __str__(self):return self.titleclass Chapter(models.Model):book = models.ForeignKey(Book,on_delete=models.CASCADE,related_name='chapters')title = models.CharField('章节标题', max_length=200)content = models.TextField('内容')order = models.PositiveIntegerField('排序')class Meta:ordering = ['order']def __str__(self):return self.title

三、表单集实现

3.1 基本表单集

python"># forms.py
from django import forms
from django.forms import formset_factory, modelformset_factory
from .models import Book, Chapterclass BookForm(forms.ModelForm):class Meta:model = Bookfields = ['title', 'isbn', 'published_date', 'price']widgets = {'published_date': forms.DateInput(attrs={'type': 'date'})}# 创建基本表单集
BookFormSet = modelformset_factory(Book,form=BookForm,extra=2,can_delete=True
)# 创建章节表单
class ChapterForm(forms.ModelForm):class Meta:model = Chapterfields = ['title', 'content', 'order']# 创建章节表单集
ChapterFormSet = modelformset_factory(Chapter,form=ChapterForm,extra=1,can_delete=True
)

3.2 内联表单集

python"># forms.py
from django.forms import inlineformset_factory# 创建内联表单集
BookChapterFormSet = inlineformset_factory(Book,Chapter,form=ChapterForm,extra=3,can_delete=True,min_num=1,validate_min=True
)class AuthorBooksForm(forms.ModelForm):class Meta:model = Authorfields = ['name', 'email', 'bio']AuthorBooksFormSet = inlineformset_factory(Author,Book,form=BookForm,extra=1,can_delete=True
)

四、视图实现

python"># views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .forms import BookFormSet, ChapterFormSet, BookChapterFormSet
from .models import Book, Authordef manage_books(request):if request.method == 'POST':formset = BookFormSet(request.POST)if formset.is_valid():formset.save()messages.success(request, '书籍信息保存成功!')return redirect('book_list')else:formset = BookFormSet()return render(request, 'books/manage_books.html', {'formset': formset})def edit_book_chapters(request, book_id):book = get_object_or_404(Book, id=book_id)if request.method == 'POST':formset = BookChapterFormSet(request.POST, instance=book)if formset.is_valid():formset.save()messages.success(request, '章节信息保存成功!')return redirect('book_detail', book_id=book.id)else:formset = BookChapterFormSet(instance=book)return render(request, 'books/edit_chapters.html', {'book': book,'formset': formset})def author_books(request, author_id):author = get_object_or_404(Author, id=author_id)if request.method == 'POST':form = AuthorBooksForm(request.POST, instance=author)formset = AuthorBooksFormSet(request.POST, instance=author)if form.is_valid() and formset.is_valid():form.save()formset.save()messages.success(request, '作者和图书信息保存成功!')return redirect('author_detail', author_id=author.id)else:form = AuthorBooksForm(instance=author)formset = AuthorBooksFormSet(instance=author)return render(request, 'books/author_books.html', {'form': form,'formset': formset})

五、模板实现

<!-- templates/books/manage_books.html -->
{% extends "base.html" %}{% block content %}
<div class="container mt-4"><h2>管理图书</h2><form method="post">{% csrf_token %}{{ formset.management_form }}<div class="formset-container">{% for form in formset %}<div class="card mb-3 book-form"><div class="card-body">{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}<div class="row"><div class="col-md-6">{{ form.title.label_tag }}{{ form.title }}</div><div class="col-md-3">{{ form.isbn.label_tag }}{{ form.isbn }}</div><div class="col-md-3">{{ form.price.label_tag }}{{ form.price }}</div></div>{% if form.can_delete %}<div class="form-check mt-2">{{ form.DELETE }}<label class="form-check-label">删除此书</label></div>{% endif %}</div></div>{% endfor %}</div><button type="submit" class="btn btn-primary">保存</button></form>
</div>
{% endblock %}<!-- JavaScript for dynamic forms -->
<script>
document.addEventListener('DOMContentLoaded', function() {const formsetContainer = document.querySelector('.formset-container');const totalForms = document.querySelector('#id_form-TOTAL_FORMS');function updateFormIndex(element, index) {element.id = element.id.replace('-__prefix__-', `-${index}-`);element.name = element.name.replace('-__prefix__-', `-${index}-`);}// Add form dynamicallyfunction addForm() {const forms = formsetContainer.querySelectorAll('.book-form');const formNum = forms.length;const newForm = forms[0].cloneNode(true);// Clear form valuesnewForm.querySelectorAll('input').forEach(input => {input.value = '';updateFormIndex(input, formNum);});formsetContainer.appendChild(newForm);totalForms.value = formNum + 1;}
});
</script>

六、表单集流程图

在这里插入图片描述

七、验证与错误处理

python"># forms.py
class BaseBookFormSet(forms.BaseModelFormSet):def clean(self):"""自定义表单集验证"""super().clean()# 验证ISBN唯一性isbns = []for form in self.forms:if form.cleaned_data and not form.cleaned_data.get('DELETE', False):isbn = form.cleaned_data.get('isbn')if isbn in isbns:raise forms.ValidationError('ISBN不能重复')isbns.append(isbn)# 使用自定义基类
BookFormSet = modelformset_factory(Book,form=BookForm,formset=BaseBookFormSet,extra=2,can_delete=True
)

八、实用工具函数

python"># utils.py
def copy_formset_instance(formset):"""复制表单集实例"""new_formset = formset.__class__(queryset=formset.queryset,initial=[form.initial for form in formset.forms],prefix=formset.prefix)return new_formsetdef get_changed_data(formset):"""获取表单集中已更改的数据"""changed_objects = []for form in formset.forms:if form.has_changed() and not form.cleaned_data.get('DELETE', False):changed_objects.append(form.instance)return changed_objects

九、常见问题和解决方案

  1. 表单集验证失败
python">def handle_formset_errors(formset):"""处理表单集错误"""errors = []for i, form in enumerate(formset):if form.errors:errors.append(f'表单 {i + 1}: {form.errors}')if formset.non_form_errors():errors.append(f'整体错误: {formset.non_form_errors()}')return errors
  1. 动态表单处理
// 动态添加和删除表单的JavaScript代码
function initDynamicFormset() {const addButton = document.getElementById('add-form');const formContainer = document.querySelector('.formset-container');const totalForms = document.getElementById('id_form-TOTAL_FORMS');addButton.addEventListener('click', function() {const forms = formContainer.getElementsByClassName('dynamic-form');const formCount = forms.length;const newForm = forms[0].cloneNode(true);// 更新表单索引newForm.innerHTML = newForm.innerHTML.replace(/form-(\d+)/g,`form-${formCount}`);formContainer.appendChild(newForm);totalForms.value = formCount + 1;});
}
  1. 文件上传处理
python">def handle_formset_files(request, formset):"""处理表单集中的文件上传"""for form in formset:if form.is_valid() and form.cleaned_data.get('file'):handle_uploaded_file(request.FILES['file'])

通过本章学习,你应该能够:

  1. 理解表单集的工作原理
  2. 使用内联表单集处理关联数据
  3. 实现动态表单添加和删除
  4. 处理表单集验证和错误

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


http://www.ppmy.cn/devtools/145981.html

相关文章

21天掌握JavaWeb - 第17天:前端页面开发与集成测试

目标 在本章节中&#xff0c;我们将学习如何根据后端API编写前端页面&#xff0c;并进行集成测试以确保前后端功能正常。 前端页面开发 核心概念 前端页面开发通常涉及HTML、CSS和JavaScript的使用&#xff0c;以构建用户界面和交互逻辑。 优势 用户体验&#xff1a;良好…

渗透测试发现漏洞产生原因以及解决方法|网络安全|渗透测试服务

渗透测试发现的问题 SQL注入攻击 原因 后端数据库过于信任前端传入数据 攻击方式&#xff1a; 通过前端输入相关SQL语句&#xff0c;使得后端数据库执行&#xff0c;套取数据库中的关键信息 解决方法&#xff1a; 对于前端传来的数据不直接进行操作数据库&#xff0c;而…

最长公共子序列【东北大学oj数据结构10-3】C++

题面 对于给定两个序列 X 和 Y , 序列 Z 是 X 和 Y 的公共子序列是指如果 Z 同时是 X 和 Y 的子序列。 例如&#xff1a;如果 X {a, b, c, b, d, a, b} 和 Y {b, d, c, a, b, a} , 那么序列 {b, c, a} 是 X 和 Y 的一个公共子序列。 但是序列 { b , c, a } 不是 X 和 Y 的最…

centos单机部署seata

文章目录 场景分析下载seata包启动 场景 centos7.9 jdk17 安装部署seata 分析 jdk和seata的版本对应关系如图 JDK版本 推荐 Seata 版本 理由 JDK 8 任何 Seata 版本 JDK 8 是 Seata 长期支持的版本&#xff0c;兼容性最好。 JDK 11 Seata 1.2.0 适合需要长期支持且性能较高的应…

如何永久解决Apache Struts文件上传漏洞

Apache Struts又双叒叕爆文件上传漏洞了。 自Apache Struts框架发布以来&#xff0c;就存在多个版本的漏洞&#xff0c;其中一些漏洞涉及到文件上传功能。这些漏洞可能允许攻击者通过构造特定的请求来绕过安全限制&#xff0c;从而上传恶意文件。虽然每次官方都发布补丁进行修…

RCE 命令执行漏洞 过滤模式 基本的过滤问题 联合ctf题目进行实践

前言 知道RCE 命令执行分为 代码执行 和 命令执行 原理 &#xff1a; 就是用户的输入被当做命令或者代码执行了 从而造成了危害 代码执行 除了eval php代码执行漏洞的函数还有 eval()、a ssert()、 preg_replace()、 create_function()、 array_map()、 call_user_func(…

JavaScript 平面坐标多边形边界线(包围盒)算法

实现&#xff1a; /*** description: 获取多边形边界(边界框或包围盒算法)* param {*} polygon 多边形顶点数组* return {*} rect 顶点数组*/getBoundingRectangle(polygon) {if (!Array.isArray(polygon) || polygon.length < 3) {throw new Error(Polygon must have at le…

leetCode43.字符串相乘

题目&#xff1a; 给定两个以字符串形式表示的非负整数num1和num2,返回num1和num2的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的BigInteger库或直接将输入转换为整数。 示例1&#xff1a; 输入&#xff1a;num1“2”,num2“3” 输出…