djangodjango-modelsdjango-admindjango-admin-tools

Django admin error ValidationError: ['ManagementForm data is missing or has been tampered with'] due to conditional inline use


I have a custom User model(AbstractUser) and a Stock model. Assume there's multiple user roles manager, supplier etc. The manager can be able to view other manager's details and supplier details.

The User model got a conditional inline which shows the stocks of each supplier if the user role is equalled to supplier.(Here role is a PositiveSmallIntegerField with choices and SUPPLIER = 2)

class SupplierStockInline(admin.StackedInline):
    """ Inline to show stocks of a supplier """

    model = Stock
    extra = 0


@admin.register(User)
class UserAdmin(UserAdmin):
    """ User """

    fieldsets = [
        (None, {'fields': ('username', 'password')}),
        ('Personal info', {'fields': (
            'first_name',
            'last_name',
            'email',
            ... some custom fields...
        )}),
        ('Permissions', {'fields': (
            'is_active',
            'is_staff',
            'is_superuser',
            'role',
            'groups',
            'user_permissions',
        )}),
        ('Important dates', {'fields': ('last_login', 'date_joined')})
    ]

    list_display = [...]
    search_fields = [...]

    # --- the source of the problem ---

    inlines = []

    def get_inlines(self, request, obj):
        """ Show inlines, stocks of supplier """
        try:
            if obj.role == SUPPLIER:
                return [SupplierStockInline]
        except:
            pass
        return []
    # --- ---- -----

This works just fine till I tried to change the role of a new user to supplier.

ValidationError: ['ManagementForm data is missing or has been tampered with']

The issue is due to that overridden get_inlines() method. When I comment out the get_inlines() it works fine and this is only happening for the role supplier. I tried to tackle this but unabled to come up with a solution.

Hoping for guidance to solve the issue, thanks in advance.


Solution

  • After hours of research finally found a solution, although I cannot exactly explain why it happening (might be related to not having related stock instances and suddenly having relations to stock instances when changed into role supplier).

    Anyway, instead of overriding the get_inlines() method, overriding change_view()and using a conditional approach can solve the problem,

    class UserAdmin(admin.ModelAdmin):
        ...
        inlines = []
    
        def change_view(self, request, object_id, form_url='', extra_context=None):
            self.inlines = []
        
            try:
                obj = self.model.objects.get(pk=object_id)
            except self.model.DoesNotExist:
                pass
            else:
                if obj.role == SUPPLIER:
                    self.inlines = [SupplierStockInline,]
            return super(UserAdmin, self).change_view(request, object_id, form_url, extra_context)