I'd like to add placeholder text to a field in the Django Admin change form. In a regular ModelForm you can do this by overriding the field's widget or by modifying self.fields['my_field'].widget
in the ModelForm __init__()
method. How do I do something similar for a Django Admin?

- 1,303
- 13
- 22
-
have a look at this link https://stackoverflow.com/questions/11411622/add-help-text-for-search-field-in-admin-py – Atlas Bravoos Oct 03 '19 at 07:40
-
Thanks @AtlasBravoos, using JavaScript is certainly one option, though it's a little bit fragile – Ben Sturmfels Oct 03 '19 at 13:32
4 Answers
The documented way is to override get_form()
:
The base implementation uses
modelform_factory()
to subclass form, modified by attributes such as fields and exclude.
If you look at the docs for modelform_factory you'll see that you can pass widgets
as kwarg. So this should work:
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
kwargs['widgets'] = {
'name': forms.TextInput(attrs={'placeholder': 'e.g. John Doe'})
}
return super().get_form(request, obj, **kwargs)
or, if you want to be sure you're not overriding any widgets (if you're inheriting from a subclass of ModelAdmin
):
kwargs['widgets'] = kwargs.get('widgets', {})
kwargs['widgets'].update({'name': ...})

- 20,112
- 2
- 29
- 42
Override the render_change_form()
method on your ModelAdmin, which provides access to the form instance:
class Address(model.Model):
street = models.CharField(max_length=50)
class AddressAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
form_instance = context['adminform'].form
form_instance.fields['street'].widget.attrs['placeholder'] = 'Your street'
return super().render_change_form(request, context, *args, **kwargs)
This approach would be the same for other field attributes like attributes like autocomplete
, autofocus
, min
, max
, required
, type
or pattern
. You also have access to context["original"]
which provides the model instance, in case you'd like to change the behavior based on the model instance.
The source code is the best reference for this: https://docs.djangoproject.com/en/2.2/_modules/django/contrib/admin/options/#ModelAdmin

- 1,303
- 13
- 22
-
Where is `render_change_form` documented? It's not part of the methods documented [here](https://docs.djangoproject.com/en/stable/ref/contrib/admin/) so it's not guaranteed to remain stable. – dirkgroten Oct 03 '19 at 15:08
-
1There's no reason to believe that `render_change_form` is in any way unstable. With `get_form` you're working with the form class, where in `render_change_form` you're working with the form instance. Access to the form instance allows you to to modify the widget, rather than having to replace it. It also provides access to the model instance in case you want to customise the form dynamically (which is very useful, but another story). – Ben Sturmfels Oct 04 '19 at 01:18
-
I meant unstable in the sense that any changes to its behaviour or signature (or even removal if the rendering logic is changed) won’t be mentioned in release notes of future django releases because it’s not part of the “official” ModelAdmin API. – dirkgroten Oct 04 '19 at 07:38
This is a way of doing it without having to manually add placeholder text to each field:
admin.py
from django import forms
class MyModelAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
form_instance = context['adminform'].form
for key, field in form_instance.fields.items():
if isinstance(field.widget, (forms.TextInput, forms.EmailInput)):
field.widget.attrs.update({'placeholder': field.label})
return super().render_change_form(request, context, *args, **kwargs)

- 3,916
- 2
- 38
- 62
Another way to do this is:
class MyModelAdmin(model.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
#--> Get form
form = super().get_form(request, obj, **kwargs)
#--> Add placeholder for field
form.base_fields['the_field_name'].widget.attrs['placeholder'] = "My_Place_Holder_Text"
#--> Return form
return form
#---
#---
This is similar to the answer of dirkgroten. The advantage here is that there is no need to worry about the widget used for the field.

- 1,416
- 12
- 27