每天40分玩转Django:Django表单集
Django表单集
一、知识要点概览表
类别 | 知识点 | 掌握程度要求 |
---|---|---|
基础概念 | FormSet、ModelFormSet | 深入理解 |
内联表单集 | InlineFormSet、BaseInlineFormSet | 熟练应用 |
表单集验证 | clean方法、验证规则 | 熟练应用 |
自定义配置 | extra、max_num、can_delete | 理解应用 |
动态管理 | JavaScript动态添加/删除表单 | 掌握使用 |
二、基础模型和表单设置
# models.py
from django.db import modelsclass Author(models.Model):name = models.CharField(max_length=100)email = models.EmailField()bio = models.TextField()def __str__(self):return self.nameclass Book(models.Model):author = models.ForeignKey(Author, on_delete=models.CASCADE)title = models.CharField(max_length=200)isbn = models.CharField(max_length=13)publication_date = models.DateField()price = models.DecimalField(max_digits=10, decimal_places=2)def __str__(self):return self.title# forms.py
from django import forms
from .models import Author, Bookclass AuthorForm(forms.ModelForm):class Meta:model = Authorfields = ['name', 'email', 'bio']class BookForm(forms.ModelForm):class Meta:model = Bookfields = ['title', 'isbn', 'publication_date', 'price']
三、基本表单集实现
1. 创建表单集
# forms.py
from django.forms import modelformset_factory, formset_factory# 创建Book模型的表单集
BookFormSet = modelformset_factory(Book,form=BookForm,extra=2, # 额外空表单数量max_num=5, # 最大表单数量can_delete=True # 允许删除
)# 创建自定义表单集
class BaseBookFormSet(forms.BaseModelFormSet):def clean(self):super().clean()titles = []for form in self.forms:if form.cleaned_data:title = form.cleaned_data.get('title')if title in titles:raise forms.ValidationError("书籍标题不能重复")titles.append(title)# 使用自定义表单集基类
BookFormSet = modelformset_factory(Book,form=BookForm,formset=BaseBookFormSet,extra=2
)
2. 视图实现
# views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import BookFormSet, AuthorFormclass BookFormSetView(View):template_name = 'books/book_formset.html'def get(self, request):formset = BookFormSet(queryset=Book.objects.none())return render(request, self.template_name, {'formset': formset})def post(self, request):formset = BookFormSet(request.POST)if formset.is_valid():instances = formset.save()messages.success(request, f'成功保存{len(instances)}本书籍信息')return redirect('book_list')return render(request, self.template_name, {'formset': formset})def manage_books(request, author_id):author = get_object_or_404(Author, id=author_id)if request.method == 'POST':formset = BookFormSet(request.POST,queryset=Book.objects.filter(author=author))if formset.is_valid():books = formset.save(commit=False)for book in books:book.author = authorbook.save()# 处理删除的书籍for obj in formset.deleted_objects:obj.delete()return redirect('author_detail', pk=author.pk)else:formset = BookFormSet(queryset=Book.objects.filter(author=author))return render(request, 'books/manage_books.html', {'formset': formset,'author': author})
四、内联表单集实现
1. 创建内联表单集
# forms.py
from django.forms import inlineformset_factory# 创建Author-Book内联表单集
BookInlineFormSet = inlineformset_factory(Author, # 父模型Book, # 子模型form=BookForm,extra=2,max_num=5,can_delete=True
)# 自定义内联表单集
class BaseBookInlineFormSet(forms.BaseInlineFormSet):def clean(self):super().clean()total_price = 0for form in self.forms:if form.cleaned_data and not form.cleaned_data.get('DELETE', False):price = form.cleaned_data.get('price', 0)total_price += priceif total_price > 1000:raise forms.ValidationError("所有书籍总价不能超过1000")# 使用自定义内联表单集
BookInlineFormSet = inlineformset_factory(Author,Book,form=BookForm,formset=BaseBookInlineFormSet,extra=2
)
2. 视图实现
# views.py
from django.views.generic.edit import UpdateView
from .forms import BookInlineFormSetclass AuthorBooksUpdateView(UpdateView):model = Authorform_class = AuthorFormtemplate_name = 'books/author_books_form.html'def get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)if self.request.POST:context['book_formset'] = BookInlineFormSet(self.request.POST,instance=self.object)else:context['book_formset'] = BookInlineFormSet(instance=self.object)return contextdef form_valid(self, form):context = self.get_context_data()book_formset = context['book_formset']if book_formset.is_valid():self.object = form.save()book_formset.instance = self.objectbook_formset.save()return redirect('author_detail', pk=self.object.pk)return self.render_to_response(self.get_context_data(form=form))
五、表单集模板实现
<!-- templates/books/author_books_form.html -->
{% extends 'base.html' %}
{% load static %}{% block content %}
<div class="container"><h1>编辑作者及其书籍</h1><form method="post">{% csrf_token %}<div class="author-form"><h2>作者信息</h2>{{ form.as_p }}</div><div class="books-formset"><h2>书籍信息</h2>{{ book_formset.management_form }}<div id="book-forms">{% for book_form in book_formset %}<div class="book-form">{{ book_form.non_field_errors }}<div class="form-row"><div class="form-group">{{ book_form.title.label_tag }}{{ book_form.title }}{{ book_form.title.errors }}</div><div class="form-group">{{ book_form.isbn.label_tag }}{{ book_form.isbn }}{{ book_form.isbn.errors }}</div><div class="form-group">{{ book_form.price.label_tag }}{{ book_form.price }}{{ book_form.price.errors }}</div>{% if book_form.instance.pk %}{{ book_form.DELETE }}{% endif %}</div></div>{% endfor %}</div><button type="button" id="add-book" class="btn btn-secondary">添加书籍</button></div><button type="submit" class="btn btn-primary mt-3">保存</button></form>
</div>{% block extra_js %}
<script>
$(document).ready(function() {// 获取表单总数const totalForms = $('#id_book_set-TOTAL_FORMS');// 添加新书籍表单$('#add-book').click(function() {const formCount = parseInt(totalForms.val());const newForm = $('#book-forms .book-form:first').clone(true);// 更新表单索引newForm.find(':input').each(function() {const name = $(this).attr('name').replace('-0-', '-' + formCount + '-');const id = 'id_' + name;$(this).attr({'name': name, 'id': id}).val('');});// 更新标签的for属性newForm.find('label').each(function() {const newFor = $(this).attr('for').replace('-0-', '-' + formCount + '-');$(this).attr('for', newFor);});// 添加新表单到DOM$('#book-forms').append(newForm);totalForms.val(formCount + 1);});
});
</script>
{% endblock %}
{% endblock %}
六、表单集处理流程图
七、高级用法示例
1. 工厂函数自定义
def get_book_formset(extra=1, max_num=None):return modelformset_factory(Book,form=BookForm,extra=extra,max_num=max_num,validate_max=True,can_delete=True,widgets={'title': forms.TextInput(attrs={'class': 'form-control'}),'isbn': forms.TextInput(attrs={'class': 'form-control'}),'price': forms.NumberInput(attrs={'class': 'form-control'})})# 在视图中使用
def manage_books_dynamic(request):BookFormSet = get_book_formset(extra=2,max_num=10)if request.method == 'POST':formset = BookFormSet(request.POST)if formset.is_valid():formset.save()return redirect('book_list')else:formset = BookFormSet()return render(request, 'books/manage_books.html', {'formset': formset})
2. 条件验证
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)# 检查总价格total_price = sum(form.cleaned_data.get('price', 0)for form in self.formsif form.cleaned_data and not form.cleaned_data.get('DELETE', False))if total_price > 1000:raise forms.ValidationError('所有书籍总价不能超过1000')
3. 动态表单处理
# views.py
from django.http import JsonResponseclass DynamicBookFormView(View):def post(self, request):if request.is_ajax():formset = BookFormSet(request.POST)if formset.is_valid():instances = formset.save()return JsonResponse({'status': 'success','message': f'成功保存{len(instances)}本书籍'})else:errors = []for form in formset:for field, error in form.errors.items():errors.append(f"{field}: {error}")return JsonResponse({'status': 'error','errors': errors})return JsonResponse({'status': 'error', 'message': '非法请求'})
这就是关于Django表单集的详细内容。通过学习这些内容,你将能够理解和使用Django的表单集系统,实现复杂的表单处理逻辑。如果有任何问题,欢迎随时提出!
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!
相关文章:

每天40分玩转Django:Django表单集
Django表单集 一、知识要点概览表 类别知识点掌握程度要求基础概念FormSet、ModelFormSet深入理解内联表单集InlineFormSet、BaseInlineFormSet熟练应用表单集验证clean方法、验证规则熟练应用自定义配置extra、max_num、can_delete理解应用动态管理JavaScript动态添加/删除表…...

查看vue的所有版本号和已安装的版本
1.使用npm查看Vue的所有版本: npm view vue versions2.查看项目中已安装的 Vue.js 版本 npm list vue...
钉钉h5微应用,鉴权提示dd.config错误说明,提示“jsapi ticket读取失败
这个提示大多是因为钉钉服务器没有成功读取到该企业的jsticket数据 1. 可能是你的企业corpid不对 登录钉钉管理后台 就可以找到对应企业的corpid 请严格使用这个corpid 。调用获取jsapi_ticket接口,使用的access_token对应的corpid和dd.config中传递的corpid不一致…...

【openGauss】正则表达式次数符号“{}“在ORACLE和openGauss中的差异
一、前言 正则作为一种常用的字符串处理方式,在各种开发语言,甚至数据库中,都有自带的正则函数。但是正则函数有很多标准,不同标准对正则表达式的解析方式不一样,本次在迁移一个ORACLE数据库到openGauss时发现了一个关…...
宏任务和微任务的区别
在 JavaScript 的异步编程模型中,宏任务(Macro Task)和微任务(Micro Task)是事件循环(Event Loop)机制中的两个重要概念。它们用于管理异步操作的执行顺序。 1. 宏任务 (Macro Task) 宏任务是较…...

数据库系统原理复习汇总
数据库系统原理复习汇总 一、数据库系统原理重点内容提纲 题型:主观题 1、简答题 第一章:数据库的基本概念:数据库、数据库管理系统、三级模式;两级映像、外码 第二章:什么是自然连接、等值连接; 第三…...

Linux day1204
五.安装lrzsz lrzsz 是用于在 Linux 系统中文件上传下载的软件。大家可能会存在疑问,我们用 MobaXterm 图形化界面就可以很方便的完成上传下载,为什么还要使用这个软件来 完成上传下载呢?实际上是这样的, Linux 的远程连接工具…...

如何在 Ubuntu 22.04 上安装并开始使用 RabbitMQ
简介 消息代理是中间应用程序,在不同服务之间提供可靠和稳定的通信方面发挥着关键作用。它们可以将传入的请求存储在队列中,并逐个提供给接收服务。通过以这种方式解耦服务,你可以使其更具可扩展性和性能。 RabbitMQ 是一种流行的开源消息代…...

【OpenGL ES】GLSL基础语法
1 前言 本文将介绍 GLSL 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符(in、out、inout)、函数参数限定符等内容,另外提供了一个 include 工具,方便多文件管理 glsl 代码&a…...

如何使用交叉编译器调试C语言程序在安卓设备中运行
一、前言 随着移动设备的普及与技术的飞速发展,越来越多的开发者面临着在Android设备上运行和调试C语言等程序的需求。然而,在软件开发的世界里,不同硬件架构对程序运行的要求千差万别,这无疑增加了开发的复杂性。特别是在移动计…...
Java全栈项目 - 智能考勤管理系统
项目介绍 智能考勤管理系统是一个基于 Java 全栈技术开发的现代化企业考勤解决方案。该系统采用前后端分离架构,实现了员工考勤、请假管理、统计分析等核心功能,旨在帮助企业提高人力资源管理效率。 技术栈 后端技术 Spring Boot 2.6.xSpring Securi…...
Linux Shell : Process Substitution
注:本文为 “Process Substitution” 相关文章合辑。 英文引文机翻,未校。 Process Substitution. 进程替换允许使用文件名引用进程的输入或输出。它采取以下形式 <(list)or >(list)进程 list 异步运行,其输入或输出显示为文件名。…...
JOGL 从入门到精通:开启 Java 3D 图形编程之旅
一、引言 Java 作为一门广泛应用的编程语言,在图形编程领域也有着强大的工具和库。JOGL(Java OpenGL)便是其中之一,它为 Java 开发者提供了访问 OpenGL(Open Graphics Library)功能的接口,使得…...
汽车网络安全基线安全研究报告
一、引言 随着汽车行业朝着智能网联方向飞速发展,汽车网络安全已成为保障用户安全和行业健康发展的关键要素。本报告将深入探讨汽车网络安全相关内容,以及国际、国内重要的汽车网络安全标准基线和相应防护措施等内容。 二、汽车网络安全的重要性 &…...

Eclipse 修改项目栏字体大小
1、菜单栏选择window->preference,然后选择General->Appearance->Colors and Fonts,在搜索栏输入"tree",点击"Edit"修改字体。 2、修改字体,选择"四号字体",点击"确定&qu…...

【PCIe 总线及设备入门学习专栏 5.1 -- PCIe 引脚 PRSNT 与热插拔】
文章目录 OverviewPRSNT 与热插拔PRSNT 硬件设计 Overview Spec 定义的热插拔是把一个PCIe卡(设备)从一个正在运行的背板或者系统中插入/或者移除。这个过程需要不影响系统的其他功能。插入的新的设备可以正确工作。 显然,这里面需要考虑的问…...

【YOLO】YOLOv5原理
概述 YOLOv5的主要架构 Backbone(主干网络):负责提取输入图像的多层次特征 Neck(颈部网络):进行特征融合和多尺度特征处理,通常包含FPN(特征金字塔网络)和PAN࿰…...

uniapp中wx.getFuzzyLocation报错如何解决
一、用wx.getLocation接口审核不通过 用uniapp开发小程序时难免需要获取当前地理位置。 代码如下: uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度: res.longitude);console.log(当前位置的纬度: r…...
opencv图像直方图
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 1、基本直方图计算 // 灰度图直方图 cv::Mat calculateGrayscaleHistogram(const cv::Mat& image) {cv::Mat histogram;int histSize 256; // 灰度级别float range[] {0, 256};const float* histRange …...

OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)
上一篇文章:OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理 目录 添加椒盐噪声 图像平滑常见处理方式 均值滤波 (blur) 方框滤波 (boxFilter) 高斯滤波 (GaussianBlur) 中值滤波 (medianBlur) 添加椒盐噪声 def add_peppersalt_noise(image, n…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...