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

如何使Django表单的外键字段只读且提交表单无报错?

问题原因

HTML的<select>控件本身不支持readonly属性,你设置的readonly会被浏览器直接忽略,所以看起来没有效果。如果改用disabled属性,虽然能禁用下拉选择,但禁用后的字段不会被表单提交到后端,会导致保存时丢失原有值。

解决方案

以下两种方法可以解决这个问题,根据你的需求选择:

方案一:禁用控件 + 后端保留原值

保留下拉框的显示样式,但禁止用户修改,同时通过后端逻辑确保原有值被正确提交:

class ApproveForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ApproveForm, self).__init__(*args, **kwargs)
        self.fields['category'].queryset = Category.objects.filter(status='active')
        self.fields['location'].queryset = Location.objects.filter(status='active')
        
        # 用disabled替代readonly,禁用下拉选择
        self.fields['location'].widget.attrs['disabled'] = True

    # 重写clean方法,强制返回location字段的原有值
    def clean_location(self):
        # 编辑已有数据时,返回实例的原有location值
        if self.instance.pk:
            return self.instance.location
        # 新增数据场景的 fallback(根据业务需求调整)
        return self.cleaned_data.get('location')

    class Meta:
        model = Request
        fields = ('item','category','location','quantity','comments' )
        widgets = {
            'item':forms.Select(attrs={'class':'form-control'}),
            'category':forms.Select(attrs={'class':'form-control'}),
            'location':forms.Select(attrs={'class':'form-control'}),
            'quantity':forms.NumberInput(attrs={'class':'form-control', 'min':'0', 'readonly':'readonly'}),
            'comments': forms.Textarea(attrs={'rows':4,'cols':5, 'class':'form-control'})
        }

方案二:替换为只读文本显示

如果不需要展示下拉框的可选范围,直接将外键字段替换为只读文本,更彻底地禁止编辑:

class ApproveForm(forms.ModelForm):
    # 定义只读字段用于显示location的文本值
    location = forms.CharField(disabled=True, required=False)

    def __init__(self, *args, **kwargs):
        super(ApproveForm, self).__init__(*args, **kwargs)
        self.fields['category'].queryset = Category.objects.filter(status='active')
        
        # 给只读字段设置初始显示值(假设Location模型有name字段,替换为你的实际字段)
        if self.instance.pk:
            self.fields['location'].initial = self.instance.location.name

    class Meta:
        model = Request
        fields = ('item','category','location','quantity','comments' )
        widgets = {
            'item':forms.Select(attrs={'class':'form-control'}),
            'category':forms.Select(attrs={'class':'form-control'}),
            'quantity':forms.NumberInput(attrs={'class':'form-control', 'min':'0', 'readonly':'readonly'}),
            'comments': forms.Textarea(attrs={'rows':4,'cols':5, 'class':'form-control'})
        }

方案对比

  • 方案一适合需要展示所有可选范围,但禁止用户修改当前值的场景;
  • 方案二更简洁,适合只需要展示当前值,不需要让用户看到其他可选选项的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:42:52