如何将Django ModelForm字段显示为不可编辑

3 投票
5 回答
12678 浏览
提问于 2025-04-16 23:45

我在学习 django ModelForm 的时候,想让用户能够编辑博客中的一条记录。这个 BlogEntry 包含了 date, postedTime, title 和 content。我想给用户展示一个编辑表单,里面有这些字段,但只有 title 和 content 是可以编辑的,而 date 和 postedTime 则是只读的,不能修改。

class BlogEntry(models.Model):
   title = models.CharField(unique=True,max_length=50)
   description = models.TextField(blank=True)
   date = models.DateField(default=datetime.date.today)
   postedTime = models.TimeField(null=True)

...

在添加记录时,我是按照正常的方式使用 ModelForm 的。

class BlogEntryAddForm(ModelForm):
    class Meta:
        model = BlogEntry
...

但是我该如何创建编辑表单呢?我希望它能 显示 date 和 postedTime 为只读(但仍然在表单中显示),同时让用户可以编辑 title 和 description

如果我在类的 Meta 中使用 exclude 来排除 date 和 postedTime,那它们就不会出现在表单上。那么,我该如何让它们显示为只读呢?

class BlogEntryEditForm(ModelForm):
    class Meta:
        model = BlogEntry
        ...?...

5 个回答

0

我这样实现的:https://djangosnippets.org/snippets/10514/ 这个实现使用了模型实例的数据来填充所有只读字段,而不是在处理表单时获取的数据。

下面是相同的代码,不过是用他的例子来展示的。

from __future__ import unicode_literals

from django.utils import six
from django.utils.encoding import force_str

__all__ = (
    'ReadOnlyFieldsMixin',
    'new_readonly_form_class'
)


class ReadOnlyFieldsMixin(object):
    """Usage:
    class MyFormAllFieldsReadOnly(ReadOnlyFieldsMixin, forms.Form):
        ...


    class MyFormSelectedFieldsReadOnly(ReadOnlyFieldsMixin, forms.Form):
        readonly_fields = ('field1', 'field2')
        ...
    """
    readonly_fields = ()

    def __init__(self, *args, **kwargs):
        super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
        self.define_readonly_fields(self.fields)

    def clean(self):
        cleaned_data = super(ReadOnlyFieldsMixin, self).clean()

        for field_name, field in six.iteritems(self.fields):
            if self._must_be_readonly(field_name):
                cleaned_data[field_name] = getattr(self.instance, field_name)

        return cleaned_data

    def define_readonly_fields(self, field_list):

        fields = [field for field_name, field in six.iteritems(field_list)
                  if self._must_be_readonly(field_name)]

        map(lambda field: self._set_readonly(field), fields)

    def _all_fields(self):
        return not bool(self.readonly_fields)

    def _set_readonly(self, field):
        field.widget.attrs['disabled'] = 'true'
        field.required = False

    def _must_be_readonly(self, field_name):
        return field_name in self.readonly_fields or self._all_fields()


def new_readonly_form_class(form_class, readonly_fields=()):
    name = force_str("ReadOnly{}".format(form_class.__name__))
    class_fields = {'readonly_fields': readonly_fields}
    return type(name, (ReadOnlyFieldsMixin, form_class), class_fields)

使用方法:

class BlogEntry(models.Model):
    title = models.CharField(unique=True,max_length=50)
    description = models.TextField(blank=True)
    date = models.DateField(default=datetime.date.today)
    postedTime = models.TimeField(null=True)


# all fields are readonly    
class BlogEntryReadOnlyForm(ReadOnlyFieldsMixin, forms.ModelForm):
    class Meta:
        model = BlogEntry

# selected fields are readonly
class BlogEntryReadOnlyForm2(ReadOnlyFieldsMixin, forms.ModelForm):
    readonly_fields = ('date', 'postedTime')
    class Meta:
        model = BlogEntry

或者使用这个函数

class BlogEntryForm(forms.ModelForm):
    class Meta:
        model = BlogEntry

BlogEntryFormReadOnlyForm = new_readonly_form_class(BlogEntryForm, readonly_fields=('description', ))
3

这里的 date 字段是用来表示这个条目是第一次创建的时间,还是最后一次修改的时间呢?如果是表示创建时间,那就用 auto_now_add 这个选项;如果是表示最后修改的时间,那就用 auto_now。也就是说:

date = models.DateField(auto_now_add=True)

在创建条目时,date 会被设置为当前时间。

auto_now_add 让这个字段变得不可编辑。如果在其他情况下需要让某个字段不可编辑,可以使用 editable 这个选项。例如:

postedDate = models.TimeField(null=True, editable=False)

另外,你可能还会在 Entry 模型中添加一个 posted 布尔字段,这样在设置 postedDate 时使用 auto_now 会很方便。每次你修改一个条目时,包括把 posted 设置为 True 的时候,postedDate 都会被更新为当前时间。

15

在表单对象中,把字段的属性设置为 readonly

form.fields['field'].widget.attrs['readonly'] = True

撰写回答