Django admin:将必填字段设为只读
Posted
技术标签:
【中文标题】Django admin:将必填字段设为只读【英文标题】:Django admin: Making a required field read-only 【发布时间】:2018-06-12 13:36:46 【问题描述】:我有许多模型与 Person
模型具有 ForeignKey 关系。例如:
class PersonData(BaseModel):
person = models.ForeignKey(Person)
data = models.TextField()
我想锁定管理员,以便在创建 PersonData
对象后,管理员用户可以更改数据,但不能更改 Person
。
起初看起来很简单——我把它放在PersonDataAdmin
类中:
def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields + ('person',)
return self.readonly_fields
在显示方面,它按预期工作——我看到person
的值,但它是灰色的,所以我无法更改它——但是当我尝试更改数据并提交表单时,我收到一条错误消息,“请更正以下错误。”没有出现其他消息,但经过一番挖掘,我发现表单缺少所需 person
字段的值。
我研究了一个解决方案,该解决方案涉及创建一个自定义表单,该表单将选择性地禁用此字段(例如 this 或 this),但是 (a) 我没有成功让它工作,并且(b) 对于看似简单得多的情况,这似乎是一大堆代码。我也研究过使用exclude
,但遇到了与read_only
相同的问题。
关于如何实现这一点的任何想法?谢谢!
【问题讨论】:
你可以在 django admin 中使用 readonly_fields 我的问题比这更复杂。 【参考方案1】:我通常做的是将模型中的字段设置为editable=False
,然后在admin.py中将字段设置为只读,就像这样,
class PersonData(BaseModel):
person = models.ForeignKey(Person, editable=False)
data = models.TextField()
然后在admin.py中
class PersonDataAdmin(admin.ModelAdmin):
readonly_fields=('person',)
希望这行得通!
【讨论】:
在这种情况下不起作用,因为该字段有一半时间是可编辑的。 将模型字段设置为editable=False
不会在管理员上显示它并跳过验证。 read_only 字段完成了这项工作。参考:docs.djangoproject.com/en/3.1/ref/models/fields/#editable【参考方案2】:
这是ModelAdmin.readonly_fields 文档。
【讨论】:
【参考方案3】:我遇到了同样的问题。一种解决方法是在 Admin 类中定义一个方法,该方法返回您要显示的只读字段的值。然后使用之前使用字段名称的方法名称。 例如而不是:
class PersonDataAdmin(admin.ModelAdmin):
readonly_fields=('person',)
使用:
class PersonDataAdmin(admin.ModelAdmin):
readonly_fields=('person_method',)
def person_method(self, obj):
return obj.person
person_method.short_description = 'Person'
short_description 设置字段的标签。如果您不设置,则该字段将标记为“人员方法”
大概在 OPs 的情况下 get_readonly_fields 变成:
def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields + ('person_method',)
return self.readonly_fields
【讨论】:
仅使用此答案中的get_readonly_fields
可以解决非方法来源(正常)字段的问题。不确定整个答案将如何工作 - 方法源字段无论如何都是只读的。【参考方案4】:
我遇到了和你完全相同的问题。检查您是否在您的管理类中使用 get_form。不知何故,这就是我的问题的原因。
【讨论】:
【参考方案5】:您可以扩展ModelAdmin.get_form()
并使用Field.disabled 来禁用person
字段,如下所示:
def get_form(self, request, obj=None, change=False, **kwargs):
form = super().get_form(request, obj, change, **kwargs)
if obj:
form.base_fields['person'].disabled = True
return form
这将防止person
字段被修改。
但是,它仍然会被设置为选择框,并且它旁边可能仍然有“更改”和“添加另一个”按钮。
这些按钮由RelatedFieldWidgetWrapper 添加。删除它们的一种方法是删除包装器:
...
field = form.base_fields['person']
field.disabled = True
if isinstance(field.widget, RelatedFieldWidgetWrapper):
# unwrap the widget
field.widget = field.widget.widget
另一种方法是设置以下小部件属性:
...
if isinstance(field.widget, RelatedFieldWidgetWrapper):
field.widget.can_add_related = False
field.widget.can_change_related = False
如果您希望将该字段设置为 readonly
字段的样式,则需要做一些额外的工作。另请参阅 fieldset.html 模板和 AdminReadonlyField 以了解 django admin 如何显示外键 readonly_fields
。
另一种方法是实现你自己的ReadOnlyWidget
,类似于this,然后设置form.base_fields['person'].widget = ReadOnlyWidget()
【讨论】:
【参考方案6】:field1 此处为必填字段,在管理屏幕中设为只读
model.py
class ModelName(models.Model):
field1 = models.ForeignKey(
ModelA,
on_delete=models.CASCADE,
db_column='col1',
related_name='col1',
verbose_name='col1'
)
field2 = models.ForeignKey(
ModelB,
on_delete=models.CASCADE,
db_column='col2',
related_name='col2',
verbose_name='col2'
)
admin.py
@admin.register(ModelName)
class AdminClassName(admin.ModelAdmin):
fields = ('field1', 'field2')
readonly_fields = ('field1')
【讨论】:
以上是关于Django admin:将必填字段设为只读的主要内容,如果未能解决你的问题,请参考以下文章