Reputation: 245
I don't want to use my own JS or alter the template when Django has other mechanisms.
I've tried three methods, first as described in how-to-add-placeholder-text-to-a-django-admin-field and in the docs.
from django import forms
class AppointmentAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
kwargs['widgets'] = {
'name': forms.TextInput(attrs={'placeholder': 'Type here'})
}
return super().get_form(request, obj, **kwargs)
Then the method described in django-add-placeholder-text-to-form-field, slightly altered to get around Python errors.
class AppointmentAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
form.base_fields['customer'].widget.attrs["placeholder"] = "Type here"
return form
Finally, using formfield_overrides:
class AppointmentAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: { "widget":forms.TextInput(attrs={'placeholder':'Type here'})},
}
None of these are working. I must be overlooking something simple?
The relevant portion of models.py:
class Appointment(models.Model):
customer = models.ForeignKey(Customer, blank=False, null=True, default=None, on_delete=models.CASCADE, related_name='appointments')
autocomplete_fields = ["customer"]
I removed the autocomplete_fields
line to no effect vis a vis a placeholder.
(Side note: I think autocomplete_fields should have a placeholder option rather than requiring all the jumping through hoops as described above.)
The 'help_text' option on the field in models.py would almost be a cheap replacement for a placeholder, except that it makes no sense in other parts of this program.
Here's the HTML that Django generates using the second method above (form.base_fields...etc.):
<div class="related-widget-wrapper" data-model-ref="customer">
<select name="customer" placeholder="Type here" required="" id="id_customer" class="admin-autocomplete select2-hidden-accessible" data-ajax--cache="true" data-ajax--delay="250" data-ajax--type="GET" data-ajax--url="/admin/autocomplete/" data-app-label="giraffe" data-model-name="appointment" data-field-name="customer" data-theme="admin-autocomplete" data-allow-clear="false" data-placeholder="" lang="en" data-select2-id="id_customer" tabindex="-1" aria-hidden="true">
</select>
<span class="select2-container select2-container--admin-autocomplete select2-container--open" style="position: absolute; top: 164.484px; left: 15px;">
<span class="select2-dropdown select2-dropdown--above" dir="ltr" style="width: 249px;">
<span class="select2-search select2-search--dropdown">
<input class="select2-search__field" type="search" tabindex="0" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="searchbox" aria-autocomplete="list" aria-controls="select2-id_customer-results">
</span>
<span class="select2-results">
<ul class="select2-results__options" role="listbox" id="select2-id_customer-results" aria-expanded="true" aria-hidden="false">
<li class="select2-results__option" role="option" aria-selected="false" data-select2-id="6">customer name here</li>
(more li and closing of spans here)
UPDATE: Django implements autocomplete select boxes using Select2, whose docs say:
For single selects only, in order for the placeholder value to appear, you must have a blank as the first option in your control. This is because the browser tries to select the first option by default. If your first option were non-empty, the browser would display this instead of the placeholder.
But Django inserts a customer as the first option. Though it's non-empty, the browser doesn't display it as described, but maybe it's still breaking the Select2 placeholder insert? Maybe the answer is how to make Django insert an empty object?
Upvotes: 0
Views: 426
Reputation: 43073
autocomplete_fields
is defined on ModelAdmin
, rather than Model
.
It is attrs['data-placeholder']
rather than attrs['placeholder']
. However, Django doesn't properly support it as data-placeholder
is overwritten in AutocompleteMixin
:
class AutocompleteMixin:
...
def build_attrs(self, base_attrs, extra_attrs=None):
...
attrs = super().build_attrs(base_attrs, extra_attrs=extra_attrs)
...
attrs.update({
...
'data-placeholder': '', # Allows clearing of the input.
...
})
return attrs
You can patch AutocompleteSelect
widget to restore data-placeholder
:
from django.contrib.admin import options
class AutocompleteSelectWithPlaceholder(options.AutocompleteSelect):
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs=extra_attrs)
if 'data-placeholder' in base_attrs:
attrs['data-placeholder'] = base_attrs['data-placeholder']
return attrs
options.AutocompleteSelect = AutocompleteSelectWithPlaceholder
Usage:
@admin.register(Appointment)
class AppointmentAdmin(admin.ModelAdmin):
autocomplete_fields = ['customer']
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
form.base_fields['customer'].widget.attrs['data-placeholder'] = 'Type here'
return form
Upvotes: 1