You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Django Crispy Forms实现模型条目表单化更新(含只读字段)?

使用Django Crispy Forms实现带只读字段的更新表单

嘿,我来帮你搞定这个需求!你想要把筛选出的ReportSample模型对象做成部分只读、仅两个字段可编辑的更新表单,用表格展示所有不可修改的属性,下面是具体的实现步骤和代码:

1. 调整表单类定义

我推荐用ModelForm来做,因为它能直接关联模型属性,比纯Form更省心。当然如果你坚持用forms.Form,我也给你准备了方案~

方式一:用ModelForm(推荐)

from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column, HTML
from .models import ReportSample

class ReportSampleForm(forms.ModelForm):
    class Meta:
        model = ReportSample
        # 把所有要展示的字段列出来,包括只读和可编辑的
        fields = ['readonly_field1', 'readonly_field2', 'readonly_field3', 'editable_field1', 'editable_field2']

    def __init__(self, *args, **kwargs):
        # 先拿到传入的目标模型实例
        self.instance = kwargs.get('instance')
        super().__init__(*args, **kwargs)
        
        # 给只读字段设置属性:禁用+只读,再加个灰色背景区分
        readonly_fields = ['readonly_field1', 'readonly_field2', 'readonly_field3']
        for field_name in readonly_fields:
            self.fields[field_name].widget.attrs.update({
                'readonly': True,
                'disabled': True,
                'style': 'background-color: #f5f5f5;'
            })

        # 配置Crispy的FormHelper,搞定表单提交和布局
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.add_input(Submit('submit', '保存更新'))
        
        # 自定义布局:先放只读字段的表格,再放可编辑字段
        self.helper.layout = Layout(
            HTML('<h3 class="mt-3">基本信息(不可修改)</h3>'),
            # 用Bootstrap表格展示只读字段
            HTML('<table class="table table-bordered">'),
            HTML('<thead><tr><th>字段名称</th><th>当前值</th></tr></thead>'),
            HTML('<tbody>'),
            # 把每个只读字段的内容塞进表格行
            HTML(f'<tr><td>字段1</td><td>{self.instance.readonly_field1}</td></tr>'),
            HTML(f'<tr><td>字段2</td><td>{self.instance.readonly_field2}</td></tr>'),
            HTML(f'<tr><td>字段3</td><td>{self.instance.readonly_field3}</td></tr>'),
            HTML('</tbody></table>'),
            
            # 可编辑字段区域
            HTML('<h3 class="mt-4">可更新内容</h3>'),
            Row(
                Column('editable_field1', css_class='form-group col-md-6 mb-0'),
                Column('editable_field2', css_class='form-group col-md-6 mb-0'),
                css_class='form-row'
            )
        )

方式二:保持用forms.Form

如果你不想改forms.Form的结构,那就手动定义所有字段,然后初始化时给只读字段赋值:

from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column, HTML
from .models import ReportSample

class ReportSampleForm(forms.Form):
    # 手动定义所有字段,只读字段用对应类型(比如CharField)
    readonly_field1 = forms.CharField(label='字段1')
    readonly_field2 = forms.CharField(label='字段2')
    readonly_field3 = forms.CharField(label='字段3')
    editable_field1 = forms.CharField(label='可编辑字段1')
    editable_field2 = forms.CharField(label='可编辑字段2')

    def __init__(self, *args, **kwargs):
        # 先取出传入的目标模型对象
        self.report_sample = kwargs.pop('report_sample')
        super().__init__(*args, **kwargs)
        
        # 给只读字段赋值并设置只读禁用属性
        readonly_fields = ['readonly_field1', 'readonly_field2', 'readonly_field3']
        for field_name in readonly_fields:
            self.fields[field_name].initial = getattr(self.report_sample, field_name)
            self.fields[field_name].widget.attrs.update({
                'readonly': True,
                'disabled': True,
                'style': 'background-color: #f5f5f5;'
            })

        # 同样配置Crispy的Helper和布局,和上面ModelForm的布局一致
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.add_input(Submit('submit', '保存更新'))
        
        self.helper.layout = Layout(
            HTML('<h3 class="mt-3">基本信息(不可修改)</h3>'),
            HTML('<table class="table table-bordered">'),
            HTML('<thead><tr><th>字段名称</th><th>当前值</th></tr></thead>'),
            HTML('<tbody>'),
            HTML(f'<tr><td>字段1</td><td>{self.report_sample.readonly_field1}</td></tr>'),
            HTML(f'<tr><td>字段2</td><td>{self.report_sample.readonly_field2}</td></tr>'),
            HTML(f'<tr><td>字段3</td><td>{self.report_sample.readonly_field3}</td></tr>'),
            HTML('</tbody></table>'),
            
            HTML('<h3 class="mt-4">可更新内容</h3>'),
            Row(
                Column('editable_field1', css_class='form-group col-md-6 mb-0'),
                Column('editable_field2', css_class='form-group col-md-6 mb-0'),
                css_class='form-row'
            )
        )

2. 视图层的处理

在视图里,你需要先筛选出目标对象,然后把它传给表单:

from django.shortcuts import get_object_or_404, render, redirect
from .forms import ReportSampleForm
from .models import ReportSample

def update_report_sample(request, pk):
    # 拿到你筛选好的目标模型对象
    report_sample = get_object_or_404(ReportSample, pk=pk)
    
    if request.method == 'POST':
        # 如果是ModelForm,直接传instance
        form = ReportSampleForm(request.POST, instance=report_sample)
        # 如果是纯Form,就传自定义的参数:
        # form = ReportSampleForm(request.POST, report_sample=report_sample)
        
        if form.is_valid():
            # ModelForm直接save就行
            form.save()
            # 纯Form的话要手动更新字段:
            # report_sample.editable_field1 = form.cleaned_data['editable_field1']
            # report_sample.editable_field2 = form.cleaned_data['editable_field2']
            # report_sample.save()
            
            # 保存后跳转到详情页或者列表页
            return redirect('report_sample_detail', pk=pk)
    else:
        # GET请求初始化表单
        form = ReportSampleForm(instance=report_sample)
        # 纯Form的话:form = ReportSampleForm(report_sample=report_sample)
    
    return render(request, 'update_report_sample.html', {'form': form})

3. 模板里渲染表单

模板很简单,只要加载Crispy的标签,然后渲染表单就行:

{% load crispy_forms_tags %}
<!DOCTYPE html>
<html>
<head>
    <title>更新报告样本</title>
    <!-- 引入Bootstrap,Crispy Forms默认适配它 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="mb-4">更新报告样本</h1>
        {% crispy form %}
    </div>
</body>
</html>

几个关键小提示

  • 只读字段设置readonly和disabled后,提交表单时这些字段不会被传到cleaned_data,完全不用担心被篡改,因为我们直接用的是模型实例的原始值。
  • 用表格展示只读字段比用禁用的表单字段更直观,用户一眼就能分清哪些能改哪些不能改。
  • 如果想要更个性化的样式,直接修改HTML里的Bootstrap类或者加自定义CSS就行。

内容的提问来源于stack exchange,提问作者trouselife

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:11:16