当前位置: 首页 > news >正文

每天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的所有版本&#xff1a; npm view vue versions2.查看项目中已安装的 Vue.js 版本 npm list vue...

钉钉h5微应用,鉴权提示dd.config错误说明,提示“jsapi ticket读取失败

这个提示大多是因为钉钉服务器没有成功读取到该企业的jsticket数据 1. 可能是你的企业corpid不对 登录钉钉管理后台 就可以找到对应企业的corpid 请严格使用这个corpid 。调用获取jsapi_ticket接口&#xff0c;使用的access_token对应的corpid和dd.config中传递的corpid不一致…...

【openGauss】正则表达式次数符号“{}“在ORACLE和openGauss中的差异

一、前言 正则作为一种常用的字符串处理方式&#xff0c;在各种开发语言&#xff0c;甚至数据库中&#xff0c;都有自带的正则函数。但是正则函数有很多标准&#xff0c;不同标准对正则表达式的解析方式不一样&#xff0c;本次在迁移一个ORACLE数据库到openGauss时发现了一个关…...

宏任务和微任务的区别

在 JavaScript 的异步编程模型中&#xff0c;宏任务&#xff08;Macro Task&#xff09;和微任务&#xff08;Micro Task&#xff09;是事件循环&#xff08;Event Loop&#xff09;机制中的两个重要概念。它们用于管理异步操作的执行顺序。 1. 宏任务 (Macro Task) 宏任务是较…...

数据库系统原理复习汇总

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

Linux day1204

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

如何在 Ubuntu 22.04 上安装并开始使用 RabbitMQ

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

【OpenGL ES】GLSL基础语法

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

如何使用交叉编译器调试C语言程序在安卓设备中运行

一、前言 随着移动设备的普及与技术的飞速发展&#xff0c;越来越多的开发者面临着在Android设备上运行和调试C语言等程序的需求。然而&#xff0c;在软件开发的世界里&#xff0c;不同硬件架构对程序运行的要求千差万别&#xff0c;这无疑增加了开发的复杂性。特别是在移动计…...

Java全栈项目 - 智能考勤管理系统

项目介绍 智能考勤管理系统是一个基于 Java 全栈技术开发的现代化企业考勤解决方案。该系统采用前后端分离架构&#xff0c;实现了员工考勤、请假管理、统计分析等核心功能&#xff0c;旨在帮助企业提高人力资源管理效率。 技术栈 后端技术 Spring Boot 2.6.xSpring Securi…...

Linux Shell : Process Substitution

注&#xff1a;本文为 “Process Substitution” 相关文章合辑。 英文引文机翻&#xff0c;未校。 Process Substitution. 进程替换允许使用文件名引用进程的输入或输出。它采取以下形式 <(list)or >(list)进程 list 异步运行&#xff0c;其输入或输出显示为文件名。…...

JOGL 从入门到精通:开启 Java 3D 图形编程之旅

一、引言 Java 作为一门广泛应用的编程语言&#xff0c;在图形编程领域也有着强大的工具和库。JOGL&#xff08;Java OpenGL&#xff09;便是其中之一&#xff0c;它为 Java 开发者提供了访问 OpenGL&#xff08;Open Graphics Library&#xff09;功能的接口&#xff0c;使得…...

汽车网络安全基线安全研究报告

一、引言 随着汽车行业朝着智能网联方向飞速发展&#xff0c;汽车网络安全已成为保障用户安全和行业健康发展的关键要素。本报告将深入探讨汽车网络安全相关内容&#xff0c;以及国际、国内重要的汽车网络安全标准基线和相应防护措施等内容。 二、汽车网络安全的重要性 &…...

Eclipse 修改项目栏字体大小

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

【PCIe 总线及设备入门学习专栏 5.1 -- PCIe 引脚 PRSNT 与热插拔】

文章目录 OverviewPRSNT 与热插拔PRSNT 硬件设计 Overview Spec 定义的热插拔是把一个PCIe卡&#xff08;设备&#xff09;从一个正在运行的背板或者系统中插入/或者移除。这个过程需要不影响系统的其他功能。插入的新的设备可以正确工作。 显然&#xff0c;这里面需要考虑的问…...

【YOLO】YOLOv5原理

概述 YOLOv5的主要架构 Backbone&#xff08;主干网络&#xff09;&#xff1a;负责提取输入图像的多层次特征 Neck&#xff08;颈部网络&#xff09;&#xff1a;进行特征融合和多尺度特征处理&#xff0c;通常包含FPN&#xff08;特征金字塔网络&#xff09;和PAN&#xff0…...

uniapp中wx.getFuzzyLocation报错如何解决

一、用wx.getLocation接口审核不通过 用uniapp开发小程序时难免需要获取当前地理位置。 代码如下&#xff1a; uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度&#xff1a; res.longitude);console.log(当前位置的纬度&#xff1a; r…...

opencv图像直方图

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 1、基本直方图计算 // 灰度图直方图 cv::Mat calculateGrayscaleHistogram(const cv::Mat& image) {cv::Mat histogram;int histSize 256; // 灰度级别float range[] {0, 256};const float* histRange …...

OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)

上一篇文章&#xff1a;OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理 目录 添加椒盐噪声 图像平滑常见处理方式 均值滤波 (blur) 方框滤波 (boxFilter) ​高斯滤波 (GaussianBlur) 中值滤波 (medianBlur) 添加椒盐噪声 def add_peppersalt_noise(image, n…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章

用 Rust 重写 Linux 内核模块实战&#xff1a;迈向安全内核的新篇章 ​​摘要&#xff1a;​​ 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言&#xff0c;受限于 C 语言本身的内存安全和并发安全问题&#xff0c;开发复杂模块极易引入难以…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...

虚拟机网络不通的问题(这里以win10的问题为主,模式NAT)

当我们网关配置好了&#xff0c;DNS也配置好了&#xff0c;最后在虚拟机里还是无法访问百度的网址。 第一种情况&#xff1a; 我们先考虑一下&#xff0c;网关的IP是否和虚拟机编辑器里的IP一样不&#xff0c;如果不一样需要更改一下&#xff0c;因为我们访问百度需要从物理机…...

Spring AI中使用ChatMemory实现会话记忆功能

文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 &#xff08;LLM&#xff09; 是无状态的&#xff0c;这就意味着他们不会保…...

Java毕业设计:办公自动化系统的设计与实现

JAVA办公自动化系统 一、系统概述 本办公自动化系统基于Java EE平台开发&#xff0c;实现了企业日常办公的数字化管理。系统包含文档管理、流程审批、会议管理、日程安排、通讯录等核心功能模块&#xff0c;采用B/S架构设计&#xff0c;支持多用户协同工作。系统使用Spring B…...

git删除本地分支和远程分支

删除本地分支 git branch -d 分支名删除远程分支 git push origin --delete 分支名...