当前位置:  开发笔记 > 后端 > 正文

在Django表单中,如何将字段只读(或禁用)以使其无法编辑?

如何解决《在Django表单中,如何将字段只读(或禁用)以使其无法编辑?》经验,为你挑选了12个好方法。

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

Daniel Naab.. 399

正如在这个答案中指出的,Django 1.9添加了Field.disabled属性:

禁用的boolean参数设置为True时,将使用禁用的HTML属性禁用表单字段,以便用户无法编辑它.即使用户篡改了提交给服务器的字段值,也会忽略该表单的初始数据中的值.

使用Django 1.8及更早版本,要禁用小部件上的条目并防止恶意POST黑客,除了readonly在表单字段上设置属性外,还必须擦除输入:

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

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

或者,替换if instance and instance.pk为表示您正在编辑的其他条件.您也可以disabled在输入字段上设置属性,而不是readonly.

clean_sku函数将确保该readonly值不会被a覆盖POST.

否则,没有内置的Django表单字段,它将在拒绝绑定的输入数据时呈现值.如果这是你想要的,你应该创建一个单独的ModelForm排除不可编辑的字段,然后在模板中打印它们.



1> Daniel Naab..:

正如在这个答案中指出的,Django 1.9添加了Field.disabled属性:

禁用的boolean参数设置为True时,将使用禁用的HTML属性禁用表单字段,以便用户无法编辑它.即使用户篡改了提交给服务器的字段值,也会忽略该表单的初始数据中的值.

使用Django 1.8及更早版本,要禁用小部件上的条目并防止恶意POST黑客,除了readonly在表单字段上设置属性外,还必须擦除输入:

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

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

或者,替换if instance and instance.pk为表示您正在编辑的其他条件.您也可以disabled在输入字段上设置属性,而不是readonly.

clean_sku函数将确保该readonly值不会被a覆盖POST.

否则,没有内置的Django表单字段,它将在拒绝绑定的输入数据时呈现值.如果这是你想要的,你应该创建一个单独的ModelForm排除不可编辑的字段,然后在模板中打印它们.


Daniel的例子的关键是测试.id字段.新创建的对象将具有id == None.顺便说一句,最古老的开放Django门票之一是关于这个问题.见http://code.djangoproject.com/ticket/342.
这个答案需要更新.在Django 1.9中添加了一个新的字段参数[`disabled`](https://docs.djangoproject.com/en/1.9/ref/forms/fields/#disabled).如果`Field.disabled`设置为'True`,则忽略该`Field`的POST值.因此,如果您使用的是1.9,则无需覆盖`clean`,只需设置`disabled = True`即可.检查[this](http://stackoverflow.com/a/34538169/3679857)的答案.
丹尼尔,谢谢你发布了答案.我不清楚如何使用这段代码?对于新的以及更新模式,这段代码不会起作用吗?你能否编辑你的答案,举例说明如何将它用于新表格和更新表格?谢谢.
在linux(ubuntu 15)/ chrome v45上,readonly将指针更改为"禁用的手",但该字段可以单击.与残疾人一样按预期工作

2> Mike Mahmud..:

Django 1.9添加了Field.disabled属性:https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled

禁用的boolean参数设置为True时,将使用禁用的HTML属性禁用表单字段,以便用户无法编辑它.即使用户篡改了提交给服务器的字段值,也会忽略该表单的初始数据中的值.


任何想法我们如何在UpdateView上使用它?因为它从模型中生成字段...
正确答案.我的解决方案类MyChangeForm(forms.ModelForm):def __init __(self,*args,**kwargs):super(MyChangeForm,self).__ init __(*args,**kwargs)self.fields ['my_field'].disabled =真正
这是一个有问题的答案 - 设置`disabled = True`将导致模型回吐给具有验证错误的用户.

3> muhuk..:

在窗口小部件上设置READONLY只会使浏览器中的输入为只读.添加返回instance.sku的clean_sku可确保字段值不会在表单级别上更改.

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

这样您就可以使用模型(未修改的保存)和aviod获取字段所需的错误.


+1这是避免更复杂的save()覆盖的好方法.但是,您希望在返回之前进行实例检查(在无换行注释模式下):"if self.instance:return self.instance.sku; else:return self.fields ['sku']"

4> chirale..:

awalker的答案对我帮助很大!

我使用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字段为读写,但在更改时,它将变为只读.



5> Humphrey..:

要使其适用于ForeignKey字段,需要进行一些更改.首先,SELECT HTML标记没有readonly属性.我们需要使用disabled ="disabled".但是,浏览器不会为该字段发回任何表单数据.因此,我们需要将该字段设置为不需要,以便字段正确验证.然后,我们需要将值重置为以前的值,因此不会将其设置为空白.

因此,对于外键,您需要执行以下操作:

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'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)

这样浏览器就不会让用户更改字段,并且总是POST,因为它保留为空白.然后,我们覆盖clean方法,将字段的值设置为最初在实例中的值.



6> StefanNch..:

对于Django 1.2+,您可以像这样覆盖该字段:

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


这也不允许在添加时编辑字段,这是原始问题的内容.

7> christophe31..:

我创建了一个可以继承的MixIn类,以便能够添加一个read_only可迭代字段,该字段将禁用非安全字段上的字段并保护字段:

(基于Daniel和Muhuk的答案)

from django import forms
from django.db.models.manager import Manager

# I used this instead of lambda expression after scope problems
def _get_cleaner(form, field):
    def clean_field():
         value = getattr(form.instance, field, None)
         if issubclass(type(value), Manager):
             value = value.all()
         return value
    return clean_field

class ROFormMixin(forms.BaseForm):
    def __init__(self, *args, **kwargs):
        super(ROFormMixin, self).__init__(*args, **kwargs)
        if hasattr(self, "read_only"):
            if self.instance and self.instance.pk:
                for field in self.read_only:
                    self.fields[field].widget.attrs['readonly'] = "readonly"
                    setattr(self, "clean_" + field, _get_cleaner(self, field))

# Basic usage
class TestForm(AModelForm, ROFormMixin):
    read_only = ('sku', 'an_other_field')



8> 小智..:

我遇到了类似的问题.看起来我能够通过在我的ModelAdmin类中定义"get_readonly_fields"方法来解决它.

像这样的东西:

# In the admin.py file

class ItemAdmin(admin.ModelAdmin):

    def get_readonly_display(self, request, obj=None):
        if obj:
            return ['sku']
        else:
            return []

好的是,obj当您添加新项时,它将是None,或者当您更改现有项时,它将是正在编辑的对象.

这里记录了get_readonly_display:http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods



9> Danny Staple..:

我刚刚为readonly字段创建了最简单的小部件 - 我真的不明白为什么表单没有这个:

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

很简单 - 让我只输出.在一个带有一堆只读值的formset中得心应手.当然 - 你也可以更聪明一点,给它一个带有attrs的div,这样你就可以为它添加类.



10> alzclarke..:

一种简单的选择是只输入form.instance.fieldName模板而不是form.fieldName



11> Evan Brumley..:

作为Humphrey帖子的有用补充,我在django-reversion中遇到了一些问题,因为它仍然将禁用的字段注册为"已更改".以下代码修复了该问题.

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'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            try:
                self.changed_data.remove('sku')
            except ValueError, e:
                pass
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)



12> 小智..:

由于我还不能评论(muhuk的解决方案),我会作为一个单独的答案作出回应.这是一个完整的代码示例,对我有用:

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

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有