Django表单集
一、知识要点概览表
类别 | 知识点 | 掌握程度要求 |
---|---|---|
基础概念 | FormSet、ModelFormSet | 深入理解 |
内联表单集 | InlineFormSet、BaseInlineFormSet | 熟练应用 |
表单集验证 | clean方法、验证规则 | 熟练应用 |
自定义配置 | extra、max_num、can_delete | 理解应用 |
动态管理 | JavaScript动态添加/删除表单 | 掌握使用 |
二、基础模型和表单设置
# models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
bio = models.TextField()
def __str__(self):
return self.name
class 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, Book
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ['name', 'email', 'bio']
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['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, AuthorForm
class 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 = author
book.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 = 0
for 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 += price
if 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 BookInlineFormSet
class AuthorBooksUpdateView(UpdateView):
model = Author
form_class = AuthorForm
template_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 context
def 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.object
book_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.forms
if 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 JsonResponse
class 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大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!
标签:form,self,40,表单,forms,book,Django,formset From: https://blog.csdn.net/weixin_40780178/article/details/144797704