在一个Django表单中,我如何使一个字段只读(或禁用),使它不能被编辑?

在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可以被重用吗?在ItemFormItem模型类中需要做哪些更改?我是否需要编写另一个类“ItemUpdateForm”来更新项目?

def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
385861 次浏览

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

当disabled布尔参数设置为True时,使用disabled 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值不会被POST覆盖。

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

在小部件上设置readonly只会使浏览器中的输入变为只读。添加返回instance.skuclean_sku可以确保字段值在表单级别上不会改变。

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

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

要使此方法适用于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方法,将字段的值设置为实例中的初始值。

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

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))
我也遇到过类似的问题。 看起来我可以通过在我的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 []

好的事情是,当你添加一个新的Item时,obj将为None,或者当你改变一个现有的Item时,它将是正在编辑的对象。

get_readonly_display被记录为在这里

作为汉弗莱的文章的一个有用的补充,我在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)

一个简单的选项是在模板中输入form.instance.fieldName而不是form.fieldName

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

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

我做了一个MixIn类,你可以继承能够添加一个read_only iterable字段,将禁用和安全的字段在非第一次编辑:

(根据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')

awalker的回答帮助了我很多!

我把他的例子改成了Django 1.3,使用get_readonly_fields

通常你应该在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字段是读写的,但在更改时它变成只读的。

再一次,我将提供另一个解决方案:)我使用汉弗莱的代码,所以这是基于那。

然而,我遇到了问题,该字段是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>

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

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

下面是一个稍微复杂一点的版本,基于christophe31的回答。它不依赖于“readonly”属性。这使得它的问题,如选择框仍然是可变的,数据提取器仍然弹出,消失了。

相反,它将表单字段小部件包装在只读小部件中,从而使表单仍然有效。原始小部件的内容显示在<span class="hidden"></span>标记中。如果小部件有render_readonly()方法,它将其用作可见文本,否则它将解析原始小部件的HTML并尝试猜测最佳表示。

import django.forms.widgets as f
import xml.etree.ElementTree as etree
from django.utils.safestring import mark_safe


def make_readonly(form):
"""
Makes all fields on the form readonly and prevents it from POST hacks.
"""


def _get_cleaner(_form, field):
def clean_field():
return getattr(_form.instance, field, None)
return clean_field


for field_name in form.fields.keys():
form.fields[field_name].widget = ReadOnlyWidget(
initial_widget=form.fields[field_name].widget)
setattr(form, "clean_" + field_name,
_get_cleaner(form, field_name))


form.is_readonly = True


class ReadOnlyWidget(f.Select):
"""
Renders the content of the initial widget in a hidden <span>. If the
initial widget has a ``render_readonly()`` method it uses that as display
text, otherwise it tries to guess by parsing the html of the initial widget.
"""


def __init__(self, initial_widget, *args, **kwargs):
self.initial_widget = initial_widget
super(ReadOnlyWidget, self).__init__(*args, **kwargs)


def render(self, *args, **kwargs):
def guess_readonly_text(original_content):
root = etree.fromstring("<span>%s</span>" % original_content)


for element in root:
if element.tag == 'input':
return element.get('value')


if element.tag == 'select':
for option in element:
if option.get('selected'):
return option.text


if element.tag == 'textarea':
return element.text


return "N/A"


original_content = self.initial_widget.render(*args, **kwargs)
try:
readonly_text = self.initial_widget.render_readonly(*args, **kwargs)
except AttributeError:
readonly_text = guess_readonly_text(original_content)


return mark_safe("""<span class="hidden">%s</span>%s""" % (
original_content, readonly_text))


# Usage example 1.
self.fields['my_field'].widget = ReadOnlyWidget(self.fields['my_field'].widget)


# Usage example 2.
form = MyForm()
make_readonly(form)

我刚刚为一个只读字段创建了一个最简单的小部件——我真的不明白为什么表单还没有这个:

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())
非常简单-只得到输出。在一个有一堆只读值的表单集中很方便。 当然,你也可以更聪明一点,给它一个带有attrs的div,这样你就可以在上面添加类了

另外两种(类似的)方法和一个通用的例子:

1)第一种方法-删除save()方法中的字段,例如(未测试;)):

def save(self, *args, **kwargs):
for fname in self.readonly_fields:
if fname in self.cleaned_data:
del self.cleaned_data[fname]
return super(<form-name>, self).save(*args,**kwargs)

2)第二种方法-在clean方法中将字段重置为初始值:

def clean_<fieldname>(self):
return self.initial[<fieldname>] # or getattr(self.instance, fieldname)

基于第二种方法,我将其概括为:

from functools                 import partial


class <Form-name>(...):


def __init__(self, ...):
...
super(<Form-name>, self).__init__(*args, **kwargs)
...
for i, (fname, field) in enumerate(self.fields.iteritems()):
if fname in self.readonly_fields:
field.widget.attrs['readonly'] = "readonly"
field.required = False
# set clean method to reset value back
clean_method_name = "clean_%s" % fname
assert clean_method_name not in dir(self)
setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))


def _clean_for_readonly_field(self, fname):
""" will reset value to initial - nothing will be changed
needs to be added dynamically - partial, see init_fields
"""
return self.initial[fname] # or getattr(self.instance, fieldname)

这是最简单的方法吗?

在视图代码中是这样的:

def resume_edit(request, r_id):
.....
r = Resume.get.object(pk=r_id)
resume = ResumeModelForm(instance=r)
.....
resume.fields['email'].widget.attrs['readonly'] = True
.....
return render(request, 'resumes/resume.html', context)

它工作得很好!

我也遇到了同样的问题,所以我创建了一个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')

如果你正在使用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的话。

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

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

当disabled布尔参数设置为True时,使用disabled HTML属性禁用表单字段,以便用户不能编辑它。即使用户篡改了提交给服务器的字段值,它也会被忽略,而由表单初始数据的值代替。

基于Yamikep的回答,我找到了一个更好的和非常简单的解决方案,它也处理ModelMultipleChoiceField字段。

form.cleaned_data中移除字段将阻止字段被保存:

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):
for f in self.readonly_fields:
self.cleaned_data.pop(f, None)
return super(ReadOnlyFieldsMixin, self).clean()

用法:

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

如果您需要多个只读字段。您可以使用下面给出的任何方法

方法1

class ItemForm(ModelForm):
readonly = ('sku',)


def __init__(self, *arg, **kwrg):
super(ItemForm, self).__init__(*arg, **kwrg)
for x in self.readonly:
self.fields[x].widget.attrs['disabled'] = 'disabled'


def clean(self):
data = super(ItemForm, self).clean()
for x in self.readonly:
data[x] = getattr(self.instance, x)
return data

方法2

继承的方法

class AdvancedModelForm(ModelForm):




def __init__(self, *arg, **kwrg):
super(AdvancedModelForm, self).__init__(*arg, **kwrg)
if hasattr(self, 'readonly'):
for x in self.readonly:
self.fields[x].widget.attrs['disabled'] = 'disabled'


def clean(self):
data = super(AdvancedModelForm, self).clean()
if hasattr(self, 'readonly'):
for x in self.readonly:
data[x] = getattr(self.instance, x)
return data




class ItemForm(AdvancedModelForm):
readonly = ('sku',)

如何使用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

对于django 1.9+
您可以使用Fields disabled参数使字段禁用。 例:在下面forms.py文件的代码片段中,我已经禁用了employee_code字段

class EmployeeForm(forms.ModelForm):
employee_code = forms.CharField(disabled=True)
class Meta:
model = Employee
fields = ('employee_code', 'designation', 'salary')
< p >参考 https://docs.djangoproject.com/en/dev/ref/forms/fields/#disabled < / p >

如果你正在使用Django ver < 1.9 (1.9已经添加了Field.disabled属性),你可以尝试在你的表单__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):
...

主要思想是,如果field是readonly,你不需要任何其他值,除了initial

附注:别忘了设置yuor_form_field.widget.attrs['readonly'] = True

你可以优雅地在小部件中添加readonly:

class SurveyModaForm(forms.ModelForm):
class Meta:
model  = Survey
fields = ['question_no']
widgets = {
'question_no':forms.NumberInput(attrs={'class':'form-control','readonly':True}),
}

今天,我在一个类似的用例中遇到了完全相同的问题。然而,我必须处理基于类的视图。基于类的视图允许继承属性和方法,从而更容易以简洁的方式重用代码。

我将通过讨论为用户创建个人资料页面所需的代码来回答您的问题。在这个页面上,他们可以更新他们的个人信息。但是,我想在不允许用户更改信息的情况下显示电子邮件字段。

是的,我本可以省略电子邮件字段,但我的强迫症不允许这样做。

在下面的例子中,我使用了一个form类和disabled = True方法的组合。这段代码在Django==2.2.7上测试。


# form class in forms.py


# Alter import User if you have created your own User class with Django default as abstract class.
from .models import User
# from django.contrib.auth.models import User


# Same goes for these forms.
from django.contrib.auth.forms import UserCreationForm, UserChangeForm




class ProfileChangeForm(UserChangeForm):


class Meta(UserCreationForm)
model = User
fields = ['first_name', 'last_name', 'email',]


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['email'].disabled = True


可以看到,已经指定了所需的用户字段。这些字段必须显示在配置文件页面上。如果需要添加其他字段,则必须在User类中指定它们,并将属性名称添加到此表单的Meta类的字段列表中。

获得所需的元数据后,调用__init__方法初始化表单。然而,在这个方法中,email字段参数“disabled”被设置为True。通过这样做,前端字段的行为被改变,导致一个只读字段,即使改变HTML代码也不能编辑。参考Field.disabled

为了完成,在下面的例子中可以看到使用表单所需的基于类的视图。


# view class in views.py


from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView, UpdateView
from django.utils.translation import gettext_lazy as _




class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'app_name/profile.html'
model = User




def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({'user': self.request.user, })
return context




class UserUpdateView(LoginRequiredMixin, SuccesMessageMixin, UpdateView):
template_name = 'app_name/update_profile.html'
model = User
form_class = ProfileChangeForm
success_message = _("Successfully updated your personal information")




def get_success_url(self):
# Please note, one has to specify a get_absolute_url() in the User class
# In my case I return:  reverse("app_name:profile")
return self.request.user.get_absolute_url()




def get_object(self, **kwargs):
return self.request.user




def form_valid(self, form):
messages.add_message(self.request, messages.INFO, _("Successfully updated your profile"))
return super().form_valid(form)




ProfileView类只显示带有用户信息的HTML页面。此外,它还拥有一个按钮,如果按下该按钮,就会转到由UserUpdateView配置的HTML页面,即“app_name/update_profile.html”。可以看到,UserUpdateView拥有两个额外的属性,即'form_class'和'success_message'。

视图知道页面上的每个字段都必须用来自User模型的数据填充。然而,通过引入'form_class'属性,视图不会得到User字段的默认布局。相反,它被重定向为通过表单类检索字段。这在灵活性方面有巨大的优势。

通过使用表单类,可以为不同的用户显示具有不同限制的不同字段。如果在模型内部设置限制,每个用户都会得到相同的待遇。

模板本身不是那么壮观,但可以在下面看到。


# HTML template in 'templates/app_name/update_profile.html'


{% extends "base.html" %}
{% load static %}
{% load crispy_form_tags %}




{% block content %}




<h1>
Update your personal information
<h1/>
<div>
<form class="form-horizontal" method="post" action="{% url 'app_name:update' %}">
{% csrf_token %}
\{\{ form|crispy }}
<div class="btn-group">
<button type="submit" class="btn btn-primary">
Update
</button>
</div>
</div>




{% endblock %}


可以看到,form标签包含一个action标签,该标签包含视图URL路由。 在按下Update按钮后,UserUpdateView被激活,并验证是否满足所有条件。如果是,将触发form_valid方法并添加一条成功消息。成功更新数据后,用户将返回到get_success_url方法中指定的URL

下面是允许视图的URL路由的代码。

# URL routing for views in urls.py


from django.urls import path
from . import views


app_name = 'app_name'


urlpatterns = [
path('profile/', view=views.ProfileView.as_view(), name='profile'),
path('update/', view=views.UserUpdateView.as_view(), name='update'),
]


你知道了。一个使用form的基于类的视图的完整实现,因此可以将电子邮件字段更改为只读和禁用。

我为这个极其详细的例子道歉。可能有更有效的方法来设计基于类的视图,但这应该是可行的。当然,有些话我可能说错了。我也还在学习。如果任何人有任何意见或改进,让我知道!

你可以这样做:

  1. 检查请求是否更新或保存一个新对象。
  2. 如果请求更新,则禁用sku字段。
  3. 如果请求是添加一个新对象,那么你必须在不禁用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.


从禁用字段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>

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

基于@paeduardo的回答(这是多余的),你可以在表单类初始化器中禁用一个字段:

class RecordForm(ModelForm):


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
var = self.fields['the_field']
var.disabled = True