简体   繁体   English

Django admin:仅在更改表单中排除字段

[英]Django admin: exclude field on change form only

If there a way to detect if information in a model is being added or changed.是否有办法检测模型中的信息是否被添加或更改。

If there is can this information be used to exclude fields.如果有,可以使用此信息来排除字段。

Some pseudocode to illustrate what I'm talking about.一些伪代码来说明我在说什么。

class SubSectionAdmin(admin.ModelAdmin):
    if something.change_or_add = 'change':
        exclude = ('field',)
    ...

Thanks谢谢

orwellian's answer will make the whole SubSectionAdmin singleton change its exclude property. orwellian 的回答将使整个 SubSectionAdmin 单例更改其排除属性。

A way to ensure that fields are excluded on a per-request basis is to do something like:确保在每个请求的基础上排除字段的方法是执行以下操作:

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)

which will just inform the Form to exclude those extra fields.这只会通知表单排除那些额外的字段。

Not sure how this will behave given a required field being excluded...不确定在排除必填字段的情况下这将如何表现......

Setting self.exclude does as @steve-pike mentions, make the whole SubSectionAdmin singleton change its exclude property.设置self.exclude就像@steve-pike 提到的那样,使整个SubSectionAdmin单例更改其排除属性。 A singleton is a class that will reuse the same instance every time the class is instantiated, so an instance is only created on the first use of the constructor, and subsequent use of the constructor will return the same instance.单例是一个类,每次实例化类时都会重用同一个实例,所以只在第一次使用构造函数时创建一个实例,后续使用构造函数将返回同一个实例。 See the wiki page for a more indept description.有关更详细的描述,请参阅wiki 页面 This means that if you write code to exclude the field on change it will have the implication that if you first add an item, the field will be there, but if you open an item for change, the field will be excluded for your following visits to the add page.这意味着,如果您编写代码以在更改时排除该字段,则会暗示如果您首先添加一个项目,该字段将在那里,但如果您打开一个项目进行更改,该字段将在您的后续访问中被排除到添加页面。

The simplest way to achieve a per request behaviour, is to use get_fields and test on the obj argument, which is None if we are adding an object, and an instance of an object if we are changing an object.实现每个请求行为的最简单方法是使用get_fields并测试obj参数,如果我们正在添加一个对象,它是None ,如果我们正在更改一个对象,则它是一个对象的实例。 The get_fields method is available from Django 1.7. 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

Update:更新:

Please note that get_fields may return a tuple, so you may need to convert fields into a list to remove elements.请注意get_fields可能返回一个元组,因此您可能需要将fields转换为列表以删除元素。 You may also encounter an error if the field name you try to remove is not in the list.如果您尝试删除的字段名称不在列表中,您也可能会遇到错误。 Therefore it may, in some cases where you have other factors that exclude fields, be better to build a set of excludes and remove using a list comprehension:因此,在某些情况下,如果您有其他因素排除字段,最好使用列表理解构建一组排除和删除:

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]

Alternatively you can also make a deepcopy of the result in the get_fieldsets method, which in other use cases may give you access to better context for excluding stuff.或者,你也可以做一个deepcopy结果中的get_fieldsets方法,这在其他使用情况可能会给你获得更好的情况下排除的东西。 Most obviously this will be useful if you need to act on the fieldset name.最明显的是,如果您需要对字段集名称进行操作,这将非常有用。 Also, this is the only way to go if you actually use fieldsets since that will omit the call to get_fields .此外,如果您实际使用get_fields这是唯一的方法,因为这将省略对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
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)

I believe you can override get_fieldsets method of ModeAdmin class.我相信您可以覆盖ModeAdmin类的get_fieldsets方法。 See the example below, in the code example below, I only want to display country field in the form when adding a new country, In order to check if object is being added, we simply need to check if obj == None , I am specifying the fields I need.看下面的例子,在下面的代码示例中,我只想在添加新国家时在表单中显示country字段,为了检查是否正在添加对象,我们只需要检查obj == None ,我是指定我需要的字段。 Now otherwise obj != None means existing object is being changed, so you can specify which fields you want to exclude from the change form.现在,否则obj != None表示正在更改现有对象,因此您可以指定要从更改表单中排除的字段。

def get_fieldsets(self, request: HttpRequest, obj=None):
    fieldset = super().get_fieldsets(request, obj=obj)
    if obj == None: # obj is None when you are adding new object.
        fieldset[0][1]["fields"] = ["country"]
    else:
        fieldset[0][1]["fields"] = [
            f.name
            for f in self.model._meta.fields
            if f.name not in ["id", "country"]
        ]
    return fieldset

You can override the get_exclude method of the admin.ModelAdmin class:您可以覆盖 admin.ModelAdmin 类的get_exclude方法:

    def get_exclude(self, request, obj):
        if "change" in request.path.split("/"):
            return [
                "fields",
                "to",
                "exclude",
            ]

        return super().get_exclude(request, obj)

I think this is cleaner than the provided answers.我认为这比提供的答案更清晰。 It doesn't override the exclude field of the Class explicitly, but rather only contextually provides the fields you wish to exclude depending on what view you're on.它不会显式覆盖类的exclude字段,而只是根据上下文提供您希望排除的字段,具体取决于您所在的视图。

The approach below has the advantage of not overriding the object wide exclude property;下面的方法具有不覆盖对象范围exclude属性的优点; instead it is reset based on each type of request相反,它会根据每种类型的请求进行重置

class SubSectionAdmin(admin.ModelAdmin):
    add_exclude = ('field1', 'field2')
    edit_exclude = ('field2',)

    def add_view(self, *args, **kwargs):
        self.exclude = getattr(self, 'add_exclude', ())
        return super(SubSectionAdmin, self).add_view(*args, **kwargs)

    def change_view(self, *args, **kwargs):
        self.exclude = getattr(self, 'edit_exclude', ())
        return super(SubSectionAdmin, self).change_view(*args, **kwargs)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM