Closed titusz closed 11 years ago
I had the same problem, this is how i fix this in one of my projects. I created additional widgets that wraps original select2 in the html with plus icon. I even asked the developer of select2 package for any other solutions and apparently this is the only one: https://github.com/applegrew/django-select2/issues/25
But keep in mind, that after item is created in popup, values in select2 widget won't reload.
# widgets.py
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.templatetags.admin_static import static
from django_select2 import Select2MultipleWidget, AutoHeavySelect2Widget
def add_plus_link(output, name, rel_to_model):
link = []
rel_to = rel_to_model
info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
related_url = reverse('admin:%s_%s_add' % info, current_app='admin')
link.append(u'<a href="%s" class="add-another" id="add_id_%s" ' % (
related_url, name))
link.append(u'onclick="return showAddAnotherPopup(this);">')
link.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
% (static('admin/img/icon_addlink.gif'), _('Add Another')))
return output + mark_safe(u''.join(link))
class PlusSelect2MultipleWidget(Select2MultipleWidget):
def render(self, name, value, attrs=None, choices=()):
output = super(PlusSelect2MultipleWidget, self).render(name, value,
attrs, choices)
return add_plus_link(output, name, self.choices.queryset.model)
class PlusAutoHeavySelect2Widget(AutoHeavySelect2Widget):
def render(self, name, value, attrs=None, choices=()):
output = super(PlusAutoHeavySelect2Widget, self).render(name, value,
attrs, choices)
return add_plus_link(output, name, self.choices.queryset.model)
That works perfectly with PlusAutoHeavySelect2Widget. Thank you very much...
Hi everyone.
I don´t know if this is still relevant, but I found a way of reloading the select2 widget after an item is created, edited or deleted as highlighted by @darklow:
But keep in mind, that after item is created in popup, values in select2 widget won't reload.
Maybe is not the most elegant, but it definitely works (on django 1.8). These are the steps:
1.- Create a js file called MyRelatedObjectsLookup.js where the key is calling $('.django-select2').djangoSelect2();
before the pop up windows closes in the functions for adding (dismissAddRelatedObjectPopup
), changing (dismissChangeRelatedObjectPopup
) and deleting (dismissDeleteRelatedObjectPopup
) an element of the djangoSelect2 widget. Here it is my MyRelatedObjectsLookup.js file:
// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.
function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
text = text.replace(/</g, '<');
text = text.replace(/>/g, '>');
text = text.replace(/"/g, '"');
text = text.replace(/'/g, "'");
text = text.replace(/&/g, '&');
return text;
}
// IE doesn't accept periods or dashes in the window name, but the element IDs
// we use to generate popup window names may contain them, therefore we map them
// to allowed characters in a reversible way so that we can locate the correct
// element when the popup window is dismissed.
function id_to_windowname(text) {
text = text.replace(/\./g, '__dot__');
text = text.replace(/\-/g, '__dash__');
return text;
}
function windowname_to_id(text) {
text = text.replace(/__dot__/g, '.');
text = text.replace(/__dash__/g, '-');
return text;
}
function showAdminPopup(triggeringLink, name_regexp) {
var name = triggeringLink.id.replace(name_regexp, '');
name = id_to_windowname(name);
var href = triggeringLink.href;
if (href.indexOf('?') == -1) {
href += '?_popup=1';
} else {
href += '&_popup=1';
}
var win = window.open(href, name, 'height=600,width=1000,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function showRelatedObjectLookupPopup(triggeringLink) {
return showAdminPopup(triggeringLink, /^lookup_/);
}
function dismissRelatedLookupPopup(win, chosenId) {
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
}
win.close();
}
function showRelatedObjectPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^(change|add|delete)_/, '');
name = id_to_windowname(name);
var href = triggeringLink.href;
var win = window.open(href, name, 'height=600,width=1000,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
// django.utils.html.escape.
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
var o;
if (elem) {
var elemName = elem.nodeName.toUpperCase();
if (elemName == 'SELECT') {
o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
} else if (elemName == 'INPUT') {
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
// Trigger a change event to update related links if required.
django.jQuery(elem).trigger('change');
} else {
var toId = name + "_to";
o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
}
// Added to update the django-select2 widgets
$('.django-select2').djangoSelect2();
win.close();
}
function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
objId = html_unescape(objId);
newRepr = html_unescape(newRepr);
var id = windowname_to_id(win.name).replace(/^edit_/, '');
var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
var selects = django.jQuery(selectsSelector);
selects.find('option').each(function() {
if (this.value == objId) {
this.innerHTML = newRepr;
this.value = newId;
}
});
// Added to update the django-select2 widgets
$('.django-select2').djangoSelect2();
win.close();
};
function dismissDeleteRelatedObjectPopup(win, objId) {
objId = html_unescape(objId);
var id = windowname_to_id(win.name).replace(/^delete_/, '');
var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
var selects = django.jQuery(selectsSelector);
selects.find('option').each(function() {
if (this.value == objId) {
django.jQuery(this).remove();
}
}).trigger('change');
// Added to update the django-select2 widgets
$('.django-select2').djangoSelect2();
win.close();
};
// Kept for backward compatibility
showAddAnotherPopup = showRelatedObjectPopup;
dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
2.- Reference it in the suit base-site.html template:
<script type="text/javascript" src="{% static 'js/MyRelatedObjectLookups.js' %}"></script>
3.- Enjoy the results
:+1:
Do you know of an easy way to retain the "add another popup" next to a select when using django-select2