在 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()
正如 this answer 中所指出的,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
来排除不可编辑的字段,然后将它们打印在您的模板中。
Django 1.9 添加了 Field.disabled 属性:https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled
disabled 布尔参数,当设置为 True 时,使用 disabled HTML 属性禁用表单字段,以便用户无法编辑它。即使用户篡改了提交给服务器的字段值,它也会被忽略,取而代之的是表单初始数据中的值。
disabled=True
将导致模型向用户吐出验证错误。
在小部件上设置 readonly
只会使浏览器中的输入变为只读。添加返回 instance.sku
的 clean_sku
可确保字段值不会在表单级别更改。
def clean_sku(self):
if self.instance:
return self.instance.sku
else:
return self.fields['sku']
这样您就可以使用模型(未修改的保存)并避免出现所需字段错误。
return self.cleaned_data['sku']
会一样好还是更好? docs 似乎建议使用 cleaned_data
:“此方法的返回值替换 cleaned_data
中的现有值,因此它必须是 cleaned_data
中的字段值(即使此方法没有更改它)或新的清洁值。”
awalker's answer对我帮助很大!
我已使用 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 []
它工作正常。现在,如果您添加一个项目,url
字段是可读写的,但在更改时它变为只读。
readonly_fields
,但它不起作用,因为我还必须有 fields
。我所做的是显示变量中的值,现在它们只是只读的。
要使这项工作适用于 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
方法以将字段的值设置为实例中的原始值。
TabularInline
中的表单,但失败了,因为 attrs
在 widget
实例之间共享,并且除第一行之外的所有实例(包括新添加的行)都呈现为只读。
对于 Django 1.2+,您可以像这样覆盖该字段:
sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))
Field
disabled
没有做我想做的事,因为它禁用了该字段,但也删除了标签/使其不可见。
我创建了一个 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')
我刚刚为只读字段创建了最简单的小部件 - 我真的不明白为什么表单还没有这个:
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,这样你就可以向它附加类。
unicode(value)
。假设 unicode dunder 是明智的,那么您就会明白这一点。
我遇到了类似的问题。看起来我可以通过在我的 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
将为无,或者当您更改现有项目时,它将是正在编辑的对象。
get_readonly_display
已记录在案 here。
get_readonly_display
现在应该是 get_readonly_fields
...
对于 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')
参考https://docs.djangoproject.com/en/dev/ref/forms/fields/#disabled
我如何使用 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
一种简单的选择是在模板中键入 form.instance.fieldName
而不是 form.fieldName
。
verbos_name
或 label
呢?如何在 django 模板中显示“标签”? @alzclarke
再一次,我将提供另一种解决方案 :) 我使用的是 Humphrey's code,所以这是基于此的。
但是,我遇到了字段是 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()
然后在模板中,您需要做一些 manual looping of the formset。
因此,在这种情况下,您将在模板中执行以下操作:
<div>
{{ form.instance.sku }} <!-- This prints the value -->
{{ form }} <!-- Prints form normally, and makes the hidden input -->
</div>
这对我来说效果更好,而且形式操作更少。
作为对 Humphrey's post 的有用补充,我在 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)
由于我还不能发表评论 (muhuk's solution),我将作为单独的答案回复。这是一个完整的代码示例,对我有用:
def clean_sku(self):
if self.instance and self.instance.pk:
return self.instance.sku
else:
return self.cleaned_data['sku']
我遇到了同样的问题,所以我创建了一个似乎适用于我的用例的 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')
'collections.OrderedDict' object has no attribute 'iteritems'
基于 Yamikep's answer,我找到了一个更好且非常简单的解决方案,它还可以处理 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')
如果您需要多个只读字段。您可以使用下面给出的任何方法
方法一
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
方法二
继承方法
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',)
您可以在小部件中优雅地添加只读:
class SurveyModaForm(forms.ModelForm):
class Meta:
model = Survey
fields = ['question_no']
widgets = {
'question_no':forms.NumberInput(attrs={'class':'form-control','readonly':True}),
}
带有一个通用示例的另外两种(相似)方法:
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)
对于 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's answer 的稍微复杂的版本。它不依赖于“只读”属性。这使得它的问题,比如选择框仍然可以改变和数据选择器仍然弹出,消失了。
相反,它将表单字段小部件包装在只读小部件中,从而使表单仍然有效。原始小部件的内容显示在 <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)
今天,我在类似的用例中遇到了完全相同的问题。但是,我不得不处理基于类的视图。基于类的视图允许继承属性和方法,从而更容易以简洁的方式重用代码。
我将通过讨论为用户创建个人资料页面所需的代码来回答您的问题。在此页面上,他们可以更新他们的个人信息。但是,我想在不允许用户更改信息的情况下显示一个电子邮件字段。
是的,我本可以省略电子邮件字段,但我的强迫症不允许这样做。
在下面的示例中,我将表单类与 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__ 方法来初始化表单。但是,在此方法中,电子邮件字段参数“已禁用”设置为 True。这样做会改变前端字段的行为,从而导致只读字段即使更改 HTML 代码也无法编辑。 Reference 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”。
视图知道页面上的每个字段都必须填充来自用户模型的数据。但是,通过引入“form_class”属性,视图不会获得用户字段的默认布局。相反,它被重定向以通过表单类检索字段。这在灵活性方面具有巨大的优势。
通过使用表单类,可以为不同的用户显示具有不同限制的不同字段。如果在模型本身内设置限制,每个用户都会得到相同的待遇。
模板本身并不那么壮观,但可以在下面看到。
# 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 %}
可以看出,表单标签包含一个包含视图 URL 路由的操作标签。按下更新按钮后,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'),
]
你有它。使用表单的基于类的视图的完整实现,因此可以将电子邮件字段更改为只读和禁用。
对于非常详细的示例,我深表歉意。可能有更有效的方法来设计基于类的视图,但这应该可行。当然,我可能对某些事情说错了。我也在学习。如果有人有任何意见或改进,请告诉我!
你可以这样做:
检查请求是更新还是保存新对象。如果请求是更新,则禁用字段 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.
这是最简单的方法吗?
就在这样的视图代码中:
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)
它工作正常!
如果您正在使用 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):
...
主要思想是,如果字段为 readonly
,您不需要除 initial
之外的任何其他值。
PS:别忘了设置yuor_form_field.widget.attrs['readonly'] = True
如果您使用的是 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',)
我认为您最好的选择是将只读属性包含在以 <span>
或 <p>
呈现的模板中,而不是将其包含在表单中(如果它是只读的)。
表格用于收集数据,而不是显示它。话虽如此,在 readonly
小部件中显示和清理 POST 数据的选项是很好的解决方案。
不定期副业成功案例分享
clean_description
方法。disabled
。如果Field.disabled
设置为True
,则忽略该Field
的 POST 值。因此,如果您使用的是 1.9,则无需覆盖clean
,只需设置disabled = True
。检查 this 答案。