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:将必填字段设为只读的主要内容,如果未能解决你的问题,请参考以下文章

django admin如何在只读字段上显示小部件

python 为Django Admin设置所有字段只读

Django Admin中的动态只读字段

在 Django 1.11 admin 中创建新对象时评估只读字段

如何在 django admin 上添加只读内联

在Django Admin中将预填充的段塞字段设置为只读