Make non-model field disabled or readonly in django admin based on condition

Question:

i have model, admin and form for it. But there is a field in my form that is not in the model and i’m doing some custom action with that field.

I want this field to be readonly or hidden or disabled for users without some permissions, but django doesn’t allow me to dynamically set any of these attributes.

My model:

class PromocodePool(TimeStampedModel):
    start = models.DateTimeField()
    end = models.DateTimeField(null=True, blank=True)

Form:

class PromocodePoolForm(forms.ModelForm):
    promocodes = forms.FileField(widget=AdminFileWidget, required=False) # this field is non-model

    class Meta:
        model = PromocodePool
        fields = '__all__'

Admin:

@admin.register(PromocodePool)
class PromocodePoolAdmin(admin.ModelAdmin):
    form = PromocodePoolForm

    list_display = ("get_start", "get_end")
    readonly_fields = (<some fields, tuple>)

    @admin.display(description="Start date")
    def get_start(self, obj):
        return _date(obj.start, formats.DATE_FORMAT)

    @admin.display(description="Start date")
    def get_end(self, obj):
        return _date(obj.end, formats.DATE_FORMAT)

    def get_readonly_fields(self, request, obj=None):
        if not request.user.has_perm("promocode.custom_permission"):
            self.readonly_fields += ("promocodes",) # this doesn't work
        return self.readonly_fields

Im getting this error:
Unable to lookup 'promocodes' on PromocodePool or PromocodePoolAdmin or PromocodePoolForm

Btw if i rename my form, error text stays the same because the real "finish" form generates via django’s ModelFormMetaclass and is called PromocodePoolForm and this form is not my form described above.

Is there any way to dynamically disable this field?

If it’s matters, im using python 3.8 and Django 3.2.6


Thanks comment below i googled further and solved my problem.

I made two form classes instead of one i had before


class PromocodePoolForm(forms.ModelForm):
    promocodes = forms.FileField(widget=AdminFileWidget, required=False, disabled=True)

    # no validation/processing for this non-model field in this class
    # just disabled field

    class Meta:
        model = PromocodePool
        fields = '__all__'

class PromocodePoolFormNotDisabled(PromocodePoolForm):
    promocodes = forms.FileField(widget=AdminFileWidget, required=False)

    # all validation/processing for non-model field is in this class now

And added this method to admin class instead of get_readonly_fields

@admin.register(PromocodePool)
class PromocodePoolAdmin(admin.ModelAdmin):
    form = PromocodePoolForm
    
    ...
    
    def get_form(self, request, obj=None, change=False, **kwargs):
        if request.user.has_perm("promocode.custom_permission"):
            self.form = PromocodePoolFormNotDisabled
        return super().get_form(request, obj, change, **kwargs)

And it worked like a charm: if user doesn’t have permission, he gets form with disabled field otherwise he gets normal one.

Asked By: RickKross

||

Answers:

It seems, that you can override the get_forms method, checking user custom permissions there, and returning your custom form. I don’t know, for shure, that it’s a good way to do this, but in my case it worked)

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.