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:
Expected:
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
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
(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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With