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.
5 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

- 2,235
- 18
- 34
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

- 91
- 1
- 4
-
This worked great in the base admin, but I can't get it to also work in an inline (`StackedInline`). Any suggestions? :) – arielkaluzhny Aug 02 '21 at 19:54
-
1Answer to my own question^^ use `get_formset` instead! Worked like a charm: ```def get_formset(self, request, obj=None, **kwargs): formset = super().get_formset(request, obj, **kwargs) field = formset.form.base_fields["impersonating"]``` – arielkaluzhny Aug 02 '21 at 20:07
-
1@arielkaluzhny I would suggest posting a separate answer so that you can show your answer with full code formatting! Cheers – Greg Sadetsky Nov 23 '21 at 01:49
-
2
(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

- 307
- 2
- 12
-
you're missing a `def` in front of the `get_formset` :-) otherwise looks great! – Greg Sadetsky Dec 16 '21 at 21:33
-
you're missing `return formset`, anyway this is what I was looking for and it works well! – Fabio Caccamo Jan 21 '22 at 11:43
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.

- 121
- 1
- 8
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['xxx'].widget.can_delete_related = False
-
5While this code snippet may solve the question, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – rene Jun 02 '19 at 15:37
-