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

当前回答

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

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

其他回答

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

我用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 version < 1.9(1.9添加了Field. conf)。禁用属性),你可以尝试向你的form __init__方法添加以下装饰器:

def bound_data_readonly(_, initial):
    return initial


def to_python_readonly(field):
    native_to_python = field.to_python

    def to_python_filed(_):
        return native_to_python(field.initial)

    return to_python_filed


def disable_read_only_fields(init_method):

    def init_wrapper(*args, **kwargs):
        self = args[0]
        init_method(*args, **kwargs)
        for field in self.fields.values():
            if field.widget.attrs.get('readonly', None):
                field.widget.attrs['disabled'] = True
                setattr(field, 'bound_data', bound_data_readonly)
                setattr(field, 'to_python', to_python_readonly(field))

    return init_wrapper


class YourForm(forms.ModelForm):

    @disable_read_only_fields
    def __init__(self, *args, **kwargs):
        ...

主要思想是,如果字段是只读的,你不需要任何其他值,除了初始值。

注:不要忘记设置yuor_form_field.widget。attrs['readonly'] = True

我也遇到了同样的问题,所以我创建了一个Mixin,它似乎适用于我的用例。

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):
        cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
        for field in self.readonly_fields:
           cleaned_data[field] = getattr(self.instance, field)

        return cleaned_data

用法,只需定义哪些必须是只读的:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')

由于我还不能评论(muhuk的解决方案),我将单独回答。这是一个完整的代码示例,适用于我:

def clean_sku(self):
  if self.instance and self.instance.pk:
    return self.instance.sku
  else:
    return self.cleaned_data['sku']