I am trying to use AutocompleteSelectMultiple from django.contrib.admin.widgets
outside the Django Admin application and meanwhile it works fine as long as there is only one of it. But in my template, I iterate through an object_list and I want to show the AutocompleteSelectMultiple for each item.
So the situation is different than on the Admin app where multiple AutocompleteSelectMultiple can occur, but they differ by field name.The request send while using the AutocompleteSelectMultiple is something like
http://localhost:8000/admin/autocomplete/?app_label=ads&model_name=my_model_name&field_name=my_field_name
In my case, there must be something else to differentiate for wich item is selected, e.g. the id
of the item. I found on Stack Overflow that it should be possible to give select2 a data-select2-id
as an attribute. How can I do that in concrete with AutocompleteSelectMultiple?
Besides that, my code shows only one occurence of the AutocompleteSelectMultiple correctly. As soon as there are more, only the last one (?!) looks and works like it should.
Here is my code:
# model.py
class Placement(models.Model):
placement = models.CharField(max_length=250, blank=False)
…
class Topic(TreeNodeModel):
treenode_display_field = "name"
name = models.CharField(max_length=50, blank=False)
bad_for_topic = models.ManyToManyField(
"Topic", related_name="bad_placement", default=None, blank=True
)
…
#forms.py
from django.contrib.admin.widgets
import AutocompleteSelectMultiple
from django.contrib import admin
from .models import Topic, KeyWord
class CustomAutocompleteSelect(AutocompleteSelectMultiple):
def __init__(
self, field, prompt="", admin_site=None, attrs=None, choices=(), using=None
):
self.prompt = prompt
super().__init__(field, admin_site, attrs=attrs, choices=choices, using=using)
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs=extra_attrs)
attrs.update(
{
"data-ajax--delay": 250,
"data-placeholder": self.prompt,
"style": "width: 100%;",
}
)
return attrs
class AddTopicForm(forms.Form):
new_topic = forms.ModelChoiceField(
queryset=Topic.objects.all(),
widget=CustomAutocompleteSelect(
KeyWord._meta.get_field("bad_for_topic"),
"Choose topic(s) …",
admin.site,
),
label="",
)
In my template, the following code renders the form (very nicely):
{% block extrahead %}
{{ block.super }}
{{ form.media }}
{% endblock %}
{{ form }}
That’s the main source of my wisdom, by the way: django - How to use the admin autocomplete field in a custom form? - Stack Overflow – and that’s how the HTML output looks like:
<span class="select2 select2-container select2-container--admin-autocomplete"
dir="ltr" data-select2-id="3" style="width: 100%;">
<span
class="selection">
<span
class="select2-selection select2-selection--multiple"
role="combobox" aria-haspopup="true" aria-expanded="false"
tabindex="-1" aria-disabled="false">
<ul class="select2-selection__rendered">
<li class="select2-search select2-search--inline"><input
class="select2-search__field" type="search" tabindex="0"
autocomplete="off" autocorrect="off"
autocapitalize="none" spellcheck="false"
role="searchbox" aria-autocomplete="list"
placeholder="Choose topic …" style="width: 687px;">
</li>
</ul>
</span>
</span>
<span class="dropdown-wrapper" aria-hidden="true"></span>
</span>
Well, it took me a while to make everything work so far and I read a lot about alternative ways, meaning not using the admin widget, see e.g. this post and this post. I even played with django-autocomplete-light, but that felt too complex somehow.
Meanwhile, the idea of simply building the HTML above in my template and to write my own handler which returns the necessary data to select2 sounds more attractive than digging deeper into AutocompleteSelectMultiple from django.contrib.admin.widgets
, what do you think?
Any idea that could help to solve this problem is very appreciated.