Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django admin: how to disable edit and delete link for foreignkey

Tags:

python

django

In django admin site, if the model has a foreignkey, by default it will be a select input and there are three links(edit, add, delete) like below. How to disable these links only for foreignkey? If using has_delete_permission, they it cannot be deleted in its own change form either.

Default: default

Expected: after

like image 271
Mas Zero Avatar asked Jun 02 '19 07:06

Mas Zero


Video Answer


4 Answers

It's not exactly a documented feature. Furthermore, formfield_for_dbfield is a damned mess (just as most of the django.contrib.admin). But it's the cleanest approach I found, and it works fine for me.

class MyAdmin(django.contrib.admin.ModelAdmin):

    def formfield_for_dbfield(self, *args, **kwargs):
        formfield = super().formfield_for_dbfield(*args, **kwargs)

        formfield.widget.can_delete_related = False
        formfield.widget.can_change_related = False
        # formfield.widget.can_add_related = False  # can change this, too
        # formfield.widget.can_view_related = False  # can change this, too

        return formfield
like image 148
Art Avatar answered Oct 30 '22 18:10

Art


in admin.py create a def under your model ModelAdmin class. Choose the options for Add, Change or Delete related to the foreign_key field, keeping the ones you desire to hide and deleting (or changing boolean from False to True) for the ones you wante to show:

class YourModelAdmin(admin.ModelAdmin):
    ...
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        field = form.base_fields["your_foreign_key_field"]
        field.widget.can_add_related = False
        field.widget.can_change_related = False
        field.widget.can_delete_related = False
        return form
like image 35
Guaracy Lima Avatar answered Oct 30 '22 19:10

Guaracy Lima


(Transferring my comment to an answer)

The above answers work great for regular admin pages. To get this to work in an admin inline (ex: admin.StackedInline), use get_formset instead of get_form:

def get_formset(self, request, obj=None, **kwargs):
     formset = super().get_formset(request, obj, **kwargs)
     field = formset.form.base_fields["your_foreign_key_field"]
     field.widget.can_add_related = False
     field.widget.can_change_related = False
     field.widget.can_delete_related = False
     return formset

like image 37
arielkaluzhny Avatar answered Oct 30 '22 17:10

arielkaluzhny


Just to add to the completeness of Art's answer:

For an Inline (admin.StackedInline, admin.TabularInline) you can use formfield_for_dbfield as well:

class MyInline(django.contrib.admin.TabularInline):

    def formfield_for_dbfield(self, *args, **kwargs):
        formfield = super().formfield_for_dbfield(*args, **kwargs)
        if formfield:
            formfield.widget.can_delete_related = False
            formfield.widget.can_change_related = False
            formfield.widget.can_add_related = False
            formfield.widget.can_view_related = False

        return formfield

formfield is somehow None sometimes for Inlines so you need to add the if statement.

like image 24
Austin Fox Avatar answered Oct 30 '22 17:10

Austin Fox