在Django表单中,我如何使字段只读(或禁用)?
当使用表单创建新条目时,应该启用所有字段——但当记录处于更新模式时,某些字段需要为只读。
例如,当创建一个新的Item模型时,所有字段都必须是可编辑的,但是在更新记录时,是否有一种方法禁用sku字段,使其可见,但不能编辑?
class Item(models.Model):
sku = models.CharField(max_length=50)
description = models.CharField(max_length=200)
added_by = models.ForeignKey(User)
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ('added_by')
def new_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Validate and save
else:
form = ItemForm()
# Render the view
ItemForm类可以重用吗?ItemForm或Item模型类需要做哪些更改?我是否需要编写另一个类“ItemUpdateForm”来更新项目?
def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
我刚刚为一个只读字段创建了一个最简单的小部件——我真的不明白为什么表单还没有这个:
class ReadOnlyWidget(widgets.Widget):
"""Some of these values are read only - just a bit of text..."""
def render(self, _, value, attrs=None):
return value
在表格中:
my_read_only = CharField(widget=ReadOnlyWidget())
非常简单,只输出结果。在一个有一堆只读值的表单集中很方便。
当然,你也可以更聪明一点,给它一个带有attrs的div,这样你就可以在上面添加类了。
基于yamiack的回答,我找到了一个更好的、非常简单的解决方案,它也可以处理ModelMultipleChoiceField字段。
从表单中删除字段。Cleaned_data禁止保存字段:
class ReadOnlyFieldsMixin(object):
readonly_fields = ()
def __init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.iteritems() if
name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = False
def clean(self):
for f in self.readonly_fields:
self.cleaned_data.pop(f, None)
return super(ReadOnlyFieldsMixin, self).clean()
用法:
class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
readonly_fields = ('field1', 'field2', 'fieldx')
对于Admin版本,如果你有多个字段,我认为这是一个更紧凑的方式:
def get_readonly_fields(self, request, obj=None):
skips = ('sku', 'other_field')
fields = super(ItemAdmin, self).get_readonly_fields(request, obj)
if not obj:
return [field for field in fields if not field in skips]
return fields
你可以这样做:
检查请求是否更新或保存一个新对象。
如果请求更新,则禁用字段sku。
如果请求是添加一个新对象,那么您必须在不禁用字段sku的情况下呈现表单。
这里有一个如何这样做的例子。
class Item(models.Model):
sku = models.CharField(max_length=50)
description = models.CharField(max_length=200)
added_by = models.ForeignKey(User)
class ItemForm(ModelForm):
def disable_sku_field(self):
elf.fields['sku'].widget.attrs['readonly'] = True
class Meta:
model = Item
exclude = ('added_by')
def new_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Just create an object or instance of the form.
# Validate and save
else:
form = ItemForm()
# Render the view
def update_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Just create an object or instance of the form.
# Validate and save
else:
form = ItemForm()
form.disable_sku_field() # call the method that will disable field.
# Render the view with the form that will have the `sku` field disabled on it.