Django admin: exclude field on change form only

后端 未结 4 1577
甜味超标
甜味超标 2020-12-13 16:23

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

相关标签:
4条回答
  • 2020-12-13 16:41

    The approach below has the advantage of not overriding the object wide exclude property; 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)
    
    0 讨论(0)
  • 2020-12-13 16:48

    orwellian's answer will make the whole SubSectionAdmin singleton change its exclude property.

    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...

    0 讨论(0)
  • 2020-12-13 16:50
    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)
    
    0 讨论(0)
  • 2020-12-13 16:56

    Setting self.exclude does as @steve-pike mentions, make the whole SubSectionAdmin singleton change its exclude property. 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. 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. The get_fields method is available from 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. 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. 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.

    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
    
    0 讨论(0)
提交回复
热议问题