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

当前回答

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

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

其他回答

对于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.

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

我用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字段是读写的,但在更改时它变成只读的。

从禁用字段mixin开始:

class ModelAllDisabledFormMixin(forms.ModelForm):
    def __init__(self, *args, **kwargs):
    '''
    This mixin to ModelForm disables all fields. Useful to have detail view based on model
    '''
    super().__init__(*args, **kwargs)
    form_fields = self.fields
    for key in form_fields.keys():
        form_fields[key].disabled = True

然后:

class MyModelAllDisabledForm(ModelAllDisabledFormMixin, forms.ModelForm):
    class Meta:
        model = MyModel
        fields = '__all__'

准备视图:

class MyModelDetailView(LoginRequiredMixin, UpdateView):
    model = MyModel
    template_name = 'my_model_detail.html'
    form_class = MyModelAllDisabledForm

把它放在my_model_detail.html模板中:

  <div class="form">
     <form method="POST" enctype="multipart/form-data">
         {% csrf_token %}
         {{ form | crispy }}
     </form>
  </div>

您将获得与更新视图中相同的表单,但禁用了所有字段。

对于Django 1.2+,你可以像这样重写字段:

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))