如何用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
相关产品推荐
相关产品推荐

