Django ModelForm 深度使用

一、ModelForm 核心价值

数据库模型
ModelForm
自动生成表单
数据验证
自动保存到数据库
减少重复代码

二、基础使用流程

1. 定义模型与ModelForm

# models.py
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    pub_date = models.DateField(auto_now_add=True)
    category = models.ForeignKey('Category', on_delete=models.CASCADE)

class Category(models.Model):
    name = models.CharField(max_length=50)

# forms.py
from django.forms import ModelForm
from .models import Article

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content', 'category']

2. 视图处理逻辑

# views.py
from django.shortcuts import render, redirect
from .forms import ArticleForm

def create_article(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save()  # 自动保存到数据库
            return redirect('article_detail', pk=article.pk)
    else:
        form = ArticleForm()
    
    return render(request, 'blog/article_form.html', {'form': form})

def update_article(request, pk):
    article = Article.objects.get(pk=pk)
    if request.method == 'POST':
        form = ArticleForm(request.POST, instance=article)
        if form.is_valid():
            form.save()
            return redirect('article_detail', pk=pk)
    else:
        form = ArticleForm(instance=article)
    
    return render(request, 'blog/article_form.html', {'form': form})

三、模板渲染优化

1. 基础模板渲染

{# templates/blog/article_form.html #}
<form method="post">
    {% csrf_token %}
    
    {{ form.as_p }}
    
    <button type="submit" class="btn btn-primary">保存</button>
</form>

2. 自定义字段渲染

<form method="post" novalidate>
    {% csrf_token %}
    
    <div class="form-group">
        {{ form.title.label_tag }}
        {{ form.title }}
        {% if form.title.help_text %}
            <small class="form-text text-muted">{{ form.title.help_text }}</small>
        {% endif %}
        {{ form.title.errors }}
    </div>
    
    <div class="form-group">
        {{ form.content.label_tag }}
        {{ form.content }}
        {{ form.content.errors }}
    </div>
</form>

四、高级配置技巧

1. Meta 配置项详解

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = '__all__'          # 包含所有字段
        exclude = ['pub_date']      # 排除指定字段
        labels = {                  # 自定义标签
            'title': '文章标题',
            'content': '正文内容'
        }
        widgets = {                 # 自定义控件
            'pub_date': forms.DateInput(attrs={'type': 'date'}),
            'content': forms.Textarea(attrs={'rows': 6})
        }
        error_messages = {          # 自定义错误信息
            'title': {'required': '标题不能为空'}
        }

2. 自定义验证逻辑

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content']

    def clean_title(self):
        title = self.cleaned_data['title']
        if '广告' in title:
            raise forms.ValidationError("标题包含敏感词汇")
        return title

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data.get('title') and len(cleaned_data['content']) < 100:
            self.add_error('content', '正文内容至少需要100字')
        return cleaned_data

五、特殊场景处理

1. 多对多关系处理

# models.py
class Tag(models.Model):
    name = models.CharField(max_length=20)

class Article(models.Model):
    tags = models.ManyToManyField(Tag)

# forms.py
class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'tags']
        widgets = {
            'tags': forms.CheckboxSelectMultiple
        }

# 视图保存时需要commit=False
def create_article(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save(commit=False)
            article.author = request.user
            article.save()
            form.save_m2m()  # 保存多对多关系
            return redirect('success')

2. 文件上传处理

# models.py
class Document(models.Model):
    title = models.CharField(max_length=100)
    file = models.FileField(upload_to='documents/')

# forms.py
class DocumentForm(ModelForm):
    class Meta:
        model = Document
        fields = ['title', 'file']

# views.py
def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('success')
    else:
        form = DocumentForm()
    return render(request, 'upload.html', {'form': form})

六、最佳实践与优化

1. 字段选择策略

# 明确列出字段(推荐)
fields = ['title', 'content']

# 排除法(慎用)
exclude = ['created_at']

# 特殊值
fields = '__all__'

2. 性能优化方案

# 选择关联字段
class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['category']
        
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['category'].queryset = Category.objects.filter(is_active=True)

3. 安全防护措施

# 防止赋值攻击
class UserProfileForm(ModelForm):
    class Meta:
        model = User
        fields = ['username', 'email']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 禁止普通用户修改权限字段
        if not self.instance.is_staff:
            del self.fields['is_staff']

七、扩展方案

1. 动态字段生成

def get_article_form(fields):
    class DynamicArticleForm(ModelForm):
        class Meta:
            model = Article
            fields = fields
    return DynamicArticleForm

# 使用示例
ArticleForm = get_article_form(['title', 'category'])

2. 模型表单集 (ModelFormset)

from django.forms import modelformset_factory

ArticleFormSet = modelformset_factory(
    Article,
    fields=['title', 'category'],
    extra=2,
    can_delete=True
)

def manage_articles(request):
    if request.method == 'POST':
        formset = ArticleFormSet(request.POST)
        if formset.is_valid():
            formset.save()
    else:
        formset = ArticleFormSet(queryset=Article.objects.filter(is_draft=True))
    
    return render(request, 'manage.html', {'formset': formset})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yant224

点滴鼓励,汇成前行星光🌟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值