Fixed #28871 -- Fixed initialization of autocomplete widgets in "Add another" inlines.
Also allowed autocomplete widgets to work on AdminSites with a name other than 'admin'.
This commit is contained in:
parent
095c1aaa89
commit
81057645f6
|
@ -220,7 +220,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass):
|
||||||
db = kwargs.get('using')
|
db = kwargs.get('using')
|
||||||
|
|
||||||
if db_field.name in self.get_autocomplete_fields(request):
|
if db_field.name in self.get_autocomplete_fields(request):
|
||||||
kwargs['widget'] = AutocompleteSelect(db_field.remote_field, using=db)
|
kwargs['widget'] = AutocompleteSelect(db_field.remote_field, self.admin_site, using=db)
|
||||||
elif db_field.name in self.raw_id_fields:
|
elif db_field.name in self.raw_id_fields:
|
||||||
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
|
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
|
||||||
elif db_field.name in self.radio_fields:
|
elif db_field.name in self.radio_fields:
|
||||||
|
@ -248,7 +248,7 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass):
|
||||||
|
|
||||||
autocomplete_fields = self.get_autocomplete_fields(request)
|
autocomplete_fields = self.get_autocomplete_fields(request)
|
||||||
if db_field.name in autocomplete_fields:
|
if db_field.name in autocomplete_fields:
|
||||||
kwargs['widget'] = AutocompleteSelectMultiple(db_field.remote_field, using=db)
|
kwargs['widget'] = AutocompleteSelectMultiple(db_field.remote_field, self.admin_site, using=db)
|
||||||
elif db_field.name in self.raw_id_fields:
|
elif db_field.name in self.raw_id_fields:
|
||||||
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
|
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
|
||||||
elif db_field.name in list(self.filter_vertical) + list(self.filter_horizontal):
|
elif db_field.name in list(self.filter_vertical) + list(self.filter_horizontal):
|
||||||
|
|
|
@ -24,15 +24,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$('.admin-autocomplete').djangoAdminSelect2();
|
// Initialize all autocomplete widgets except the one in the template
|
||||||
|
// form used when a new formset is added.
|
||||||
|
$('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('formset:added', (function() {
|
$(document).on('formset:added', (function() {
|
||||||
return function(event, $newFormset) {
|
return function(event, $newFormset) {
|
||||||
var $widget = $newFormset.find('.admin-autocomplete');
|
return $newFormset.find('.admin-autocomplete').djangoAdminSelect2();
|
||||||
// Exclude already initialized Select2 inputs.
|
|
||||||
$widget = $widget.not('.select2-hidden-accessible');
|
|
||||||
return init($widget);
|
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
}(django.jQuery));
|
}(django.jQuery));
|
||||||
|
|
|
@ -400,10 +400,11 @@ class AutocompleteMixin:
|
||||||
Renders the necessary data attributes for select2 and adds the static form
|
Renders the necessary data attributes for select2 and adds the static form
|
||||||
media.
|
media.
|
||||||
"""
|
"""
|
||||||
url_name = 'admin:%s_%s_autocomplete'
|
url_name = '%s:%s_%s_autocomplete'
|
||||||
|
|
||||||
def __init__(self, rel, attrs=None, choices=(), using=None):
|
def __init__(self, rel, admin_site, attrs=None, choices=(), using=None):
|
||||||
self.rel = rel
|
self.rel = rel
|
||||||
|
self.admin_site = admin_site
|
||||||
self.db = using
|
self.db = using
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
if attrs is not None:
|
if attrs is not None:
|
||||||
|
@ -413,7 +414,7 @@ class AutocompleteMixin:
|
||||||
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
model = self.rel.model
|
model = self.rel.model
|
||||||
return reverse(self.url_name % (model._meta.app_label, model._meta.model_name))
|
return reverse(self.url_name % (self.admin_site.name, model._meta.app_label, model._meta.model_name))
|
||||||
|
|
||||||
def build_attrs(self, base_attrs, extra_attrs=None):
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,6 +17,7 @@ PAGINATOR_SIZE = AutocompleteJsonView.paginate_by
|
||||||
|
|
||||||
|
|
||||||
class AuthorAdmin(admin.ModelAdmin):
|
class AuthorAdmin(admin.ModelAdmin):
|
||||||
|
ordering = ['id']
|
||||||
search_fields = ['id']
|
search_fields = ['id']
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,3 +230,23 @@ class SeleniumTests(AdminSeleniumTestCase):
|
||||||
search.send_keys(Keys.RETURN)
|
search.send_keys(Keys.RETURN)
|
||||||
select = Select(self.selenium.find_element_by_id('id_related_questions'))
|
select = Select(self.selenium.find_element_by_id('id_related_questions'))
|
||||||
self.assertEqual(len(select.all_selected_options), 2)
|
self.assertEqual(len(select.all_selected_options), 2)
|
||||||
|
|
||||||
|
def test_inline_add_another_widgets(self):
|
||||||
|
def assertNoResults(row):
|
||||||
|
elem = row.find_element_by_css_selector('.select2-selection')
|
||||||
|
elem.click() # Open the autocomplete dropdown.
|
||||||
|
results = self.selenium.find_element_by_css_selector('.select2-results')
|
||||||
|
self.assertTrue(results.is_displayed())
|
||||||
|
option = self.selenium.find_element_by_css_selector('.select2-results__option')
|
||||||
|
self.assertEqual(option.text, 'No results found')
|
||||||
|
|
||||||
|
# Autocomplete works in rows present when the page loads.
|
||||||
|
self.selenium.get(self.live_server_url + reverse('autocomplete_admin:admin_views_book_add'))
|
||||||
|
rows = self.selenium.find_elements_by_css_selector('.dynamic-authorship_set')
|
||||||
|
self.assertEqual(len(rows), 3)
|
||||||
|
assertNoResults(rows[0])
|
||||||
|
# Autocomplete works in rows added using the "Add another" button.
|
||||||
|
self.selenium.find_element_by_link_text('Add another Authorship').click()
|
||||||
|
rows = self.selenium.find_elements_by_css_selector('.dynamic-authorship_set')
|
||||||
|
self.assertEqual(len(rows), 4)
|
||||||
|
assertNoResults(rows[-1])
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib import admin
|
||||||
from django.contrib.admin.widgets import AutocompleteSelect
|
from django.contrib.admin.widgets import AutocompleteSelect
|
||||||
from django.forms import ModelChoiceField
|
from django.forms import ModelChoiceField
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
@ -14,10 +15,12 @@ class AlbumForm(forms.ModelForm):
|
||||||
widgets = {
|
widgets = {
|
||||||
'band': AutocompleteSelect(
|
'band': AutocompleteSelect(
|
||||||
Album._meta.get_field('band').remote_field,
|
Album._meta.get_field('band').remote_field,
|
||||||
|
admin.site,
|
||||||
attrs={'class': 'my-class'},
|
attrs={'class': 'my-class'},
|
||||||
),
|
),
|
||||||
'featuring': AutocompleteSelect(
|
'featuring': AutocompleteSelect(
|
||||||
Album._meta.get_field('featuring').remote_field,
|
Album._meta.get_field('featuring').remote_field,
|
||||||
|
admin.site,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ class AlbumForm(forms.ModelForm):
|
||||||
class NotRequiredBandForm(forms.Form):
|
class NotRequiredBandForm(forms.Form):
|
||||||
band = ModelChoiceField(
|
band = ModelChoiceField(
|
||||||
queryset=Album.objects.all(),
|
queryset=Album.objects.all(),
|
||||||
widget=AutocompleteSelect(Album._meta.get_field('band').remote_field),
|
widget=AutocompleteSelect(Album._meta.get_field('band').remote_field, admin.site),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +36,7 @@ class NotRequiredBandForm(forms.Form):
|
||||||
class RequiredBandForm(forms.Form):
|
class RequiredBandForm(forms.Form):
|
||||||
band = ModelChoiceField(
|
band = ModelChoiceField(
|
||||||
queryset=Album.objects.all(),
|
queryset=Album.objects.all(),
|
||||||
widget=AutocompleteSelect(Album._meta.get_field('band').remote_field),
|
widget=AutocompleteSelect(Album._meta.get_field('band').remote_field, admin.site),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,7 +71,7 @@ class AutocompleteMixinTests(TestCase):
|
||||||
|
|
||||||
def test_get_url(self):
|
def test_get_url(self):
|
||||||
rel = Album._meta.get_field('band').remote_field
|
rel = Album._meta.get_field('band').remote_field
|
||||||
w = AutocompleteSelect(rel)
|
w = AutocompleteSelect(rel, admin.site)
|
||||||
url = w.get_url()
|
url = w.get_url()
|
||||||
self.assertEqual(url, '/admin_widgets/band/autocomplete/')
|
self.assertEqual(url, '/admin_widgets/band/autocomplete/')
|
||||||
|
|
||||||
|
@ -130,4 +133,4 @@ class AutocompleteMixinTests(TestCase):
|
||||||
else:
|
else:
|
||||||
expected_files = base_files
|
expected_files = base_files
|
||||||
with translation.override(lang):
|
with translation.override(lang):
|
||||||
self.assertEqual(AutocompleteSelect(rel).media._js, expected_files)
|
self.assertEqual(AutocompleteSelect(rel, admin.site).media._js, expected_files)
|
||||||
|
|
Loading…
Reference in New Issue