Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using exclude with Django fieldsets

I'm trying to write a dynamic form for models that allow users with different permissions to do different things. What I would like is for the below code to function properly, so that non-superusers can't edit any of the fields in the 'Merchant' block.

class CategoryModelAdmin(LWModelAdmin):
    fieldsets = (
        ('Merchant', {'fields': ('merchant',) }),
        ('Category', { 'fields': ('name', 'parent',) }),
    )
    def get_form(self,request,obj=None, **kwargs): 
        if request.user.is_superuser:
            self.exclude = []
        else:
            self.exclude = ['Merchant']
        return super(CategoryModelAdmin,self).get_form(request, obj=None, **kwargs)

While I can achieve that effect via the code below, I'm really searching for the "right" way to do it and it feels like using exclude is the way to go, but I can't seem to get it right no matter what I try.

class CategoryModelAdmin(LWModelAdmin):
    declared_fieldsets = None
    admin_fieldsets = (
        (None, {'fields': ('merchant',) }),
        ('Category', { 'fields': ('name', 'parent',) }),
    )
    restricted_fieldsets = (
        ('Category', { 'fields': ('name', 'parent',) }),
    )

    def get_fieldsets(self, request, obj=None):
        if request.user.is_superuser:
            fieldsets = self.admin_fieldsets
        else:
            fieldsets = self.restricted_fieldsets
        return LWModelAdmin.fieldsets + fieldsets

    def get_form(self, request, obj=None, **kwargs):
        self.declared_fieldsets = self.get_fieldsets(request, obj)
        return super(self.__class__, self).get_form(request, obj)
like image 991
Ceasar Bautista Avatar asked Oct 10 '22 10:10

Ceasar Bautista


2 Answers

Your code is not thread-safe, you shouldn't set attributes on self (self.exclude etc) in your custom ModelAdmin methods. Instead use kwargs of ModelAdmin.get_form and InlineModelAdmin.get_formset to get what you want.

Here's a simple example:

class CategoryModelAdmin(LWModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            kwargs['exclude'] = ['foo', 'bar',]
        else:
            kwargs['fields'] = ['foo',]
        return super(CategoryModelAdmin, self).get_form(request, obj, **kwargs)
like image 67
shanyu Avatar answered Oct 14 '22 04:10

shanyu


I'm not sure if this would be more or less of a hack, but have you considered this?

Create two different models, pointing to the same table. Each model can have it's own ModelAdmin class with whatever settings you want. Then use Django's permissions to make one of them invisible to non-administrators.

If you want to avoid duplicating code, you can derive both of your classes from some common base class you create.

The advantage is that you wouldn't be doing anything outside of what's written in the documentation. I don't think we're meant to be overwriting methods like get_form and get_fieldsets. If they're not truly part of the published API, those methods may change or be removed on day, and you could get owned in an upgrade.

Just a thought...

like image 26
Mike M. Lin Avatar answered Oct 14 '22 04:10

Mike M. Lin