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

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

Django表单集

一、今日学习内容概述

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

二、基本模型定义

# models.py
from django.db import models

class Author(models.Model):
    name = models.CharField('姓名', max_length=100)
    email = models.EmailField('邮箱')
    bio = models.TextField('简介', blank=True)

    def __str__(self):
        return self.name

class 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.title

class 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 基本表单集

# forms.py
from django import forms
from django.forms import formset_factory, modelformset_factory
from .models import Book, Chapter

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = ['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 = Chapter
        fields = ['title', 'content', 'order']

# 创建章节表单集
ChapterFormSet = modelformset_factory(
    Chapter,
    form=ChapterForm,
    extra=1,
    can_delete=True
)

3.2 内联表单集

# 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 = Author
        fields = ['name', 'email', 'bio']

AuthorBooksFormSet = inlineformset_factory(
    Author,
    Book,
    form=BookForm,
    extra=1,
    can_delete=True
)

四、视图实现

# 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, Author

def 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 dynamically
    function addForm() {
        const forms = formsetContainer.querySelectorAll('.book-form');
        const formNum = forms.length;
        const newForm = forms[0].cloneNode(true);
        
        // Clear form values
        newForm.querySelectorAll('input').forEach(input => {
            input.value = '';
            updateFormIndex(input, formNum);
        });
        
        formsetContainer.appendChild(newForm);
        totalForms.value = formNum + 1;
    }
});
</script>

六、表单集流程图

在这里插入图片描述

七、验证与错误处理

# 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
)

八、实用工具函数

# 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_formset

def 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. 表单集验证失败
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. 文件上传处理
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.kler.cn/a/452379.html

相关文章:

  • kubernates实战
  • EasyPoi 使用$fe:模板语法生成Word动态行
  • python+reportlab创建PDF文件
  • openwrt 负载均衡方法 openwrt负载均衡本地源接口
  • 基于Spring Boot的工商局商家管理系统
  • 如何在 Ubuntu 22.04 上安装以及使用 MongoDB
  • 在 Mac M2 上安装 PyTorch 并启用 MPS 加速的详细教程与性能对比
  • 使用Python探索量子机器学习
  • ByConity BSP 解锁数据仓库新未来
  • Android DRM 技术详解与应用实践
  • HarmonyOS NEXT 实战之元服务:静态案例效果--- 手机一键加速、手机垃圾清理
  • 中阳智能:量化交易助力科技与金融融合
  • 基于LSTM长短期记忆神经网络的多分类预测【MATLAB】
  • 跟我学c++中级篇——C++中的缓存利用
  • 达梦数据库-数据共享集群部署
  • vue3导入excel并解析excel数据渲染到表格中,纯前端实现。
  • CSS 居中技术完全指南:从基础到高级应用
  • SpeedTree For UE5学习笔记
  • 分布式Python计算服务MaxFrame使用心得
  • <代码随想录> 算法训练营-2024.12.25
  • Linux零基础速成篇一(理论+实操)
  • 强力巨彩租赁屏技术更新,适用多种户外应用场景
  • xtu oj 1614 数字(加强版)
  • 【Super Tilemap Editor使用详解】(十一):画笔(Brushes)
  • SSE 流式场景应用 及 方案总结
  • 教育行业 UI 设计基础篇:简洁直观的风格打造