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

