Django admin: 仅在更改表单中排除字段
有没有办法检测一个模型中的信息是否被添加或更改了呢?
如果有的话,这些信息能不能用来排除某些字段?
下面是一些伪代码,来说明我想表达的意思。
class SubSectionAdmin(admin.ModelAdmin):
if something.change_or_add = 'change':
exclude = ('field',)
...
谢谢
6 个回答
设置 self.exclude
就像 @steve-pike 提到的那样,会让整个 SubSectionAdmin
的单例改变它的排除属性。单例是一种类,每次创建这个类的实例时都会重用同一个实例,也就是说,只有在第一次使用构造函数时会创建一个实例,之后再使用构造函数时会返回同一个实例。想了解更多,可以查看这个 维基页面。
这意味着,如果你写代码来在更改时排除某个字段,那么如果你第一次添加一个项目,这个字段会存在,但如果你打开一个项目进行更改,这个字段在你后续访问添加页面时就会被排除。
实现每次请求行为的最简单方法是使用 get_fields
方法,并根据 obj
参数进行测试。如果我们是在添加一个对象,obj
就是 None
;如果我们是在更改一个对象,obj
就是一个对象的实例。get_fields
方法从 Django 1.7 开始提供。
class SubSectionAdmin(admin.ModelAdmin):
def get_fields(self, request, obj=None):
fields = super(SubSectionAdmin, self).get_fields(request, obj)
if obj: # obj will be None on the add page, and something on change pages
fields.remove('field')
return fields
更新:
请注意,get_fields
可能会返回一个元组,所以你可能需要将 fields
转换成列表来移除某些元素。如果你尝试移除的字段名不在列表中,你可能会遇到错误。因此,在某些情况下,如果你有其他因素排除字段,构建一个排除集合并使用列表推导式来移除可能会更好:
class SubSectionAdmin(admin.ModelAdmin):
def get_fields(self, request, obj=None):
fields = list(super(SubSectionAdmin, self).get_fields(request, obj))
exclude_set = set()
if obj: # obj will be None on the add page, and something on change pages
exclude_set.add('field')
return [f for f in fields if f not in exclude_set]
另外,你也可以在 get_fieldsets
方法中对结果进行 deepcopy
,在其他用例中这可能会让你更好地了解排除某些内容的上下文。最明显的是,如果你需要对字段集名称进行操作,这会非常有用。而且,如果你实际上使用字段集,这是唯一的做法,因为这样会省去对 get_fields
的调用。
from copy import deepcopy
class SubSectionAdmin(admin.ModelAdmin):
def get_fieldsets(self, request, obj=None):
"""Custom override to exclude fields"""
fieldsets = deepcopy(super(SubSectionAdmin, self).get_fieldsets(request, obj))
# Append excludes here instead of using self.exclude.
# When fieldsets are defined for the user admin, so self.exclude is ignored.
exclude = ()
if not request.user.is_superuser:
exclude += ('accepted_error_margin_alert', 'accepted_error_margin_warning')
# Iterate fieldsets
for fieldset in fieldsets:
fieldset_fields = fieldset[1]['fields']
# Remove excluded fields from the fieldset
for exclude_field in exclude:
if exclude_field in fieldset_fields:
fieldset_fields = tuple(field for field in fieldset_fields if field != exclude_field) # Filter
fieldset[1]['fields'] = fieldset_fields # Store new tuple
return fieldsets
orwellian的回答会让整个SubSectionAdmin这个单例的排除属性发生变化。
为了确保在每次请求时都能排除特定字段,可以这样做:
class SubSectionAdmin(admin.ModelAdmin):
# ...
def get_form(self, request, obj=None, **kwargs):
"""Override the get_form and extend the 'exclude' keyword arg"""
if obj:
kwargs.update({
'exclude': getattr(kwargs, 'exclude', tuple()) + ('field',),
})
return super(SubSectionAdmin, self).get_form(request, obj, **kwargs)
这样做只是告诉表单要排除那些额外的字段。
不过,我不太确定如果排除了一个必填字段会有什么样的表现……
class SubSectionAdmin(admin.ModelAdmin):
# ...
def change_view(self, request, object_id, extra_context=None):
self.exclude = ('field', )
return super(SubSectionAdmin, self).change_view(request, object_id, extra_context)
当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。