在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()

当前回答

如果你正在使用Django admin,这里有一个最简单的解决方案。

class ReadonlyFieldsMixin(object):
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return super(ReadonlyFieldsMixin, self).get_readonly_fields(request, obj)
        else:
            return tuple()

class MyAdmin(ReadonlyFieldsMixin, ModelAdmin):
    readonly_fields = ('sku',)

其他回答

我认为你最好的选择是在你的模板中包含readonly属性,在<span>或<p>中呈现,而不是在表单中包含它,如果它是readonly的话。

表单用于收集数据,而不是显示数据。也就是说,在只读小部件中显示和清除POST数据的选项是很好的解决方案。

在小部件上设置readonly只会使浏览器中的输入变为只读。添加一个返回实例的clean_sku。Sku确保字段值不会在表单级别上改变。

def clean_sku(self):
    if self.instance: 
        return self.instance.sku
    else: 
        return self.fields['sku']

这样您就可以使用model的(未修改的保存)并避免获得字段所需的错误。

如何使用Django 1.11:

class ItemForm(ModelForm):
    disabled_fields = ('added_by',)

    class Meta:
        model = Item
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        for field in self.disabled_fields:
            self.fields[field].disabled = True

沃克的回答对我帮助很大!

我用get_readonly_fields修改了他的例子,让它在Django 1.3中工作。

通常你应该在app/admin.py中声明这样的东西:

class ItemAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('url',)

我是这样适应的:

# In the admin.py file
class ItemAdmin(admin.ModelAdmin):
    ...
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['url']
        else:
            return []

它工作得很好。现在,如果您添加一个Item, url字段是读写的,但在更改时它变成只读的。

再一次,我将提供另一个解决方案:)我正在使用Humphrey的代码,所以这是基于它。

但是,我遇到了一个ModelChoiceField字段的问题。对于第一个请求,一切都会正常工作。但是,如果表单集试图添加一个新项,但验证失败,那么“现有”表单就出了问题,其中SELECTED选项被重置为默认的---------。

总之,我不知道怎么解决这个问题。所以相反,(我认为这实际上是更干净的形式),我使字段HiddenInputField()。这只是意味着您必须在模板中多做一些工作。

所以我的解决方案是简化表单:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].widget=HiddenInput()

然后在模板中,您需要对该格式集进行一些手动循环。

所以,在这种情况下,你会在模板中做这样的事情:

<div>
    {{ form.instance.sku }} <!-- This prints the value -->
    {{ form }} <!-- Prints form normally, and makes the hidden input -->
</div>

这对我来说工作得更好一点,而且形式操作更少。