mirror of
https://github.com/django/django.git
synced 2025-11-18 02:56:45 +00:00
Merge 6e5d9762be into 1ce6e78dd4
This commit is contained in:
commit
d247f4954e
11 changed files with 134 additions and 63 deletions
1
AUTHORS
1
AUTHORS
|
|
@ -952,6 +952,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Scott Pashley <github@scottpashley.co.uk>
|
Scott Pashley <github@scottpashley.co.uk>
|
||||||
scott@staplefish.com
|
scott@staplefish.com
|
||||||
Sean Brant
|
Sean Brant
|
||||||
|
Sean Helvey <me@seanhelvey.com>
|
||||||
Sebastian Hillig <sebastian.hillig@gmail.com>
|
Sebastian Hillig <sebastian.hillig@gmail.com>
|
||||||
Sebastian Spiegel <https://www.tivix.com/>
|
Sebastian Spiegel <https://www.tivix.com/>
|
||||||
Segyo Myung <myungsekyo@gmail.com>
|
Segyo Myung <myungsekyo@gmail.com>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from urllib.parse import quote as urlquote
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers, widgets
|
from django.contrib.admin import helpers, widgets
|
||||||
|
|
@ -71,6 +72,7 @@ from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
IS_POPUP_VAR = "_popup"
|
IS_POPUP_VAR = "_popup"
|
||||||
|
SOURCE_MODEL_VAR = "_source_model"
|
||||||
TO_FIELD_VAR = "_to_field"
|
TO_FIELD_VAR = "_to_field"
|
||||||
IS_FACETS_VAR = "_facets"
|
IS_FACETS_VAR = "_facets"
|
||||||
|
|
||||||
|
|
@ -1342,6 +1344,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
"save_on_top": self.save_on_top,
|
"save_on_top": self.save_on_top,
|
||||||
"to_field_var": TO_FIELD_VAR,
|
"to_field_var": TO_FIELD_VAR,
|
||||||
"is_popup_var": IS_POPUP_VAR,
|
"is_popup_var": IS_POPUP_VAR,
|
||||||
|
"source_model_var": SOURCE_MODEL_VAR,
|
||||||
"app_label": app_label,
|
"app_label": app_label,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -1398,12 +1401,39 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
else:
|
else:
|
||||||
attr = obj._meta.pk.attname
|
attr = obj._meta.pk.attname
|
||||||
value = obj.serializable_value(attr)
|
value = obj.serializable_value(attr)
|
||||||
popup_response_data = json.dumps(
|
popup_response = {
|
||||||
{
|
"value": str(value),
|
||||||
"value": str(value),
|
"obj": str(obj),
|
||||||
"obj": str(obj),
|
}
|
||||||
}
|
|
||||||
)
|
# Find the optgroup for the new item, if available
|
||||||
|
source_model_name = request.POST.get(SOURCE_MODEL_VAR)
|
||||||
|
|
||||||
|
if source_model_name:
|
||||||
|
if "." not in source_model_name:
|
||||||
|
raise ValueError(f"Invalid model format: {source_model_name}")
|
||||||
|
app_label, model_name = source_model_name.split(".", 1)
|
||||||
|
try:
|
||||||
|
source_model = apps.get_model(app_label, model_name)
|
||||||
|
except LookupError:
|
||||||
|
raise LookupError(f"No installed app/model: {source_model_name}")
|
||||||
|
form_class = self.admin_site._registry[source_model].form
|
||||||
|
if (
|
||||||
|
hasattr(form_class, "_meta")
|
||||||
|
and hasattr(form_class._meta, "fields")
|
||||||
|
and form_class._meta.fields is not None
|
||||||
|
and self.opts.verbose_name_plural in form_class._meta.fields
|
||||||
|
):
|
||||||
|
field_choices = (
|
||||||
|
form_class().fields[self.opts.verbose_name_plural].choices
|
||||||
|
)
|
||||||
|
|
||||||
|
for optgroup_label, optgroup_choices in field_choices:
|
||||||
|
for choice_value, choice_display in optgroup_choices:
|
||||||
|
if choice_display == str(obj):
|
||||||
|
popup_response["optgroup"] = optgroup_label
|
||||||
|
|
||||||
|
popup_response_data = json.dumps(popup_response)
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request,
|
request,
|
||||||
self.popup_response_template
|
self.popup_response_template
|
||||||
|
|
@ -1913,6 +1943,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
"object_id": object_id,
|
"object_id": object_id,
|
||||||
"original": obj,
|
"original": obj,
|
||||||
"is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET,
|
"is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET,
|
||||||
|
"source_model": request.GET.get(SOURCE_MODEL_VAR),
|
||||||
"to_field": to_field,
|
"to_field": to_field,
|
||||||
"media": media,
|
"media": media,
|
||||||
"inline_admin_formsets": inline_formsets,
|
"inline_admin_formsets": inline_formsets,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
{
|
{
|
||||||
|
const getOptionGroupName = (option) => option.parentElement.label;
|
||||||
const SelectBox = {
|
const SelectBox = {
|
||||||
cache: {},
|
cache: {},
|
||||||
init: function(id) {
|
init: function(id) {
|
||||||
|
|
@ -7,20 +8,29 @@
|
||||||
SelectBox.cache[id] = [];
|
SelectBox.cache[id] = [];
|
||||||
const cache = SelectBox.cache[id];
|
const cache = SelectBox.cache[id];
|
||||||
for (const node of box.options) {
|
for (const node of box.options) {
|
||||||
cache.push({value: node.value, text: node.text, displayed: 1});
|
const group = getOptionGroupName(node);
|
||||||
|
cache.push({group, value: node.value, text: node.text, displayed: 1});
|
||||||
}
|
}
|
||||||
|
SelectBox.sort(id);
|
||||||
},
|
},
|
||||||
redisplay: function(id) {
|
redisplay: function(id) {
|
||||||
// Repopulate HTML select box from cache
|
// Repopulate HTML select box from cache
|
||||||
const box = document.getElementById(id);
|
const box = document.getElementById(id);
|
||||||
const scroll_value_from_top = box.scrollTop;
|
const scroll_value_from_top = box.scrollTop;
|
||||||
box.innerHTML = '';
|
box.innerHTML = '';
|
||||||
for (const node of SelectBox.cache[id]) {
|
let node = box;
|
||||||
if (node.displayed) {
|
let group = null;
|
||||||
const new_option = new Option(node.text, node.value, false, false);
|
for (const option of SelectBox.cache[id]) {
|
||||||
// Shows a tooltip when hovering over the option
|
if (option.group && option.group !== group && option.displayed) {
|
||||||
new_option.title = node.text;
|
group = option.group;
|
||||||
box.appendChild(new_option);
|
node = document.createElement('optgroup');
|
||||||
|
node.setAttribute('label', option.group);
|
||||||
|
box.appendChild(node);
|
||||||
|
}
|
||||||
|
if (option.displayed) {
|
||||||
|
const new_option = new Option(option.text, option.value, false, false);
|
||||||
|
new_option.title = option.text;
|
||||||
|
node.appendChild(new_option);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
box.scrollTop = scroll_value_from_top;
|
box.scrollTop = scroll_value_from_top;
|
||||||
|
|
@ -57,7 +67,8 @@
|
||||||
cache.splice(delete_index, 1);
|
cache.splice(delete_index, 1);
|
||||||
},
|
},
|
||||||
add_to_cache: function(id, option) {
|
add_to_cache: function(id, option) {
|
||||||
SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
|
SelectBox.cache[id].push({group: option.group, value: option.value, text: option.text, displayed: 1});
|
||||||
|
SelectBox.sort(id);
|
||||||
},
|
},
|
||||||
cache_contains: function(id, value) {
|
cache_contains: function(id, value) {
|
||||||
// Check if an item is contained in the cache
|
// Check if an item is contained in the cache
|
||||||
|
|
@ -73,7 +84,8 @@
|
||||||
for (const option of from_box.options) {
|
for (const option of from_box.options) {
|
||||||
const option_value = option.value;
|
const option_value = option.value;
|
||||||
if (option.selected && SelectBox.cache_contains(from, option_value)) {
|
if (option.selected && SelectBox.cache_contains(from, option_value)) {
|
||||||
SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
|
const group = getOptionGroupName(option);
|
||||||
|
SelectBox.add_to_cache(to, {group, value: option_value, text: option.text, displayed: 1});
|
||||||
SelectBox.delete_from_cache(from, option_value);
|
SelectBox.delete_from_cache(from, option_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +97,8 @@
|
||||||
for (const option of from_box.options) {
|
for (const option of from_box.options) {
|
||||||
const option_value = option.value;
|
const option_value = option.value;
|
||||||
if (SelectBox.cache_contains(from, option_value)) {
|
if (SelectBox.cache_contains(from, option_value)) {
|
||||||
SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
|
const group = getOptionGroupName(option);
|
||||||
|
SelectBox.add_to_cache(to, {group, value: option_value, text: option.text, displayed: 1});
|
||||||
SelectBox.delete_from_cache(from, option_value);
|
SelectBox.delete_from_cache(from, option_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,8 +107,8 @@
|
||||||
},
|
},
|
||||||
sort: function(id) {
|
sort: function(id) {
|
||||||
SelectBox.cache[id].sort(function(a, b) {
|
SelectBox.cache[id].sort(function(a, b) {
|
||||||
a = a.text.toLowerCase();
|
a = (a.group && a.group.toLowerCase() || '') + a.text.toLowerCase();
|
||||||
b = b.text.toLowerCase();
|
b = (b.group && b.group.toLowerCase() || '') + b.text.toLowerCase();
|
||||||
if (a > b) {
|
if (a > b) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
|
function dismissAddRelatedObjectPopup(win, newId, newRepr, optgroup) {
|
||||||
const name = removePopupIndex(win.name);
|
const name = removePopupIndex(win.name);
|
||||||
const elem = document.getElementById(name);
|
const elem = document.getElementById(name);
|
||||||
if (elem) {
|
if (elem) {
|
||||||
|
|
@ -143,8 +143,9 @@
|
||||||
} else {
|
} else {
|
||||||
const toId = name + "_to";
|
const toId = name + "_to";
|
||||||
const toElem = document.getElementById(toId);
|
const toElem = document.getElementById(toId);
|
||||||
const o = new Option(newRepr, newId);
|
const newOption = new Option(newRepr, newId);
|
||||||
SelectBox.add_to_cache(toId, o);
|
newOption.group = optgroup;
|
||||||
|
SelectBox.add_to_cache(toId, newOption);
|
||||||
SelectBox.redisplay(toId);
|
SelectBox.redisplay(toId);
|
||||||
if (toElem && toElem.nodeName.toUpperCase() === 'SELECT') {
|
if (toElem && toElem.nodeName.toUpperCase() === 'SELECT') {
|
||||||
const skipIds = [name + "_from"];
|
const skipIds = [name + "_from"];
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
opener.dismissDeleteRelatedObjectPopup(window, initData.value);
|
opener.dismissDeleteRelatedObjectPopup(window, initData.value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj);
|
opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj, initData.optgroup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
<div>
|
<div>
|
||||||
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
|
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
|
||||||
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
|
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
|
||||||
|
{% if source_model %}<input type="hidden" name="{{ source_model_var }}" value="{{ source_model }}">{% endif %}
|
||||||
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
|
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
|
||||||
{% if errors %}
|
{% if errors %}
|
||||||
<p class="errornote">
|
<p class="errornote">
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from django.contrib.admin.exceptions import (
|
||||||
from django.contrib.admin.options import (
|
from django.contrib.admin.options import (
|
||||||
IS_FACETS_VAR,
|
IS_FACETS_VAR,
|
||||||
IS_POPUP_VAR,
|
IS_POPUP_VAR,
|
||||||
|
SOURCE_MODEL_VAR,
|
||||||
TO_FIELD_VAR,
|
TO_FIELD_VAR,
|
||||||
IncorrectLookupParameters,
|
IncorrectLookupParameters,
|
||||||
ShowFacets,
|
ShowFacets,
|
||||||
|
|
@ -49,6 +50,7 @@ IGNORED_PARAMS = (
|
||||||
SEARCH_VAR,
|
SEARCH_VAR,
|
||||||
IS_FACETS_VAR,
|
IS_FACETS_VAR,
|
||||||
IS_POPUP_VAR,
|
IS_POPUP_VAR,
|
||||||
|
SOURCE_MODEL_VAR,
|
||||||
TO_FIELD_VAR,
|
TO_FIELD_VAR,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -332,16 +332,24 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_context(self, name, value, attrs):
|
def get_context(self, name, value, attrs):
|
||||||
from django.contrib.admin.views.main import IS_POPUP_VAR, TO_FIELD_VAR
|
from django.contrib.admin.views.main import (
|
||||||
|
IS_POPUP_VAR,
|
||||||
|
SOURCE_MODEL_VAR,
|
||||||
|
TO_FIELD_VAR,
|
||||||
|
)
|
||||||
|
|
||||||
rel_opts = self.rel.model._meta
|
rel_opts = self.rel.model._meta
|
||||||
info = (rel_opts.app_label, rel_opts.model_name)
|
info = (rel_opts.app_label, rel_opts.model_name)
|
||||||
related_field_name = self.rel.get_related_field().name
|
related_field_name = self.rel.get_related_field().name
|
||||||
|
app_label = self.rel.field.model._meta.app_label
|
||||||
|
model_name = self.rel.field.model._meta.model_name
|
||||||
|
|
||||||
url_params = "&".join(
|
url_params = "&".join(
|
||||||
"%s=%s" % param
|
"%s=%s" % param
|
||||||
for param in [
|
for param in [
|
||||||
(TO_FIELD_VAR, related_field_name),
|
(TO_FIELD_VAR, related_field_name),
|
||||||
(IS_POPUP_VAR, 1),
|
(IS_POPUP_VAR, 1),
|
||||||
|
(SOURCE_MODEL_VAR, f"{app_label}.{model_name}"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
context = {
|
context = {
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,16 @@ QUnit.test('preserve scroll position', function(assert) {
|
||||||
assert.equal(toSelectBox.options.length, selectedOptions.length);
|
assert.equal(toSelectBox.options.length, selectedOptions.length);
|
||||||
assert.notEqual(fromSelectBox.scrollTop, 0);
|
assert.notEqual(fromSelectBox.scrollTop, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('retain optgroups', function(assert) {
|
||||||
|
const $ = django.jQuery;
|
||||||
|
$('<select id="id"></select>').appendTo('#qunit-fixture');
|
||||||
|
const grp = $('<optgroup label="group one">').appendTo('#id');
|
||||||
|
$('<option value="0">A</option>').appendTo(grp);
|
||||||
|
$('</optgroup>').appendTo('#id');
|
||||||
|
$('<option value="1">B</option>').appendTo('#id');
|
||||||
|
SelectBox.init('id');
|
||||||
|
SelectBox.redisplay('id');
|
||||||
|
assert.equal($('#id option').length, 2);
|
||||||
|
assert.equal($('#id optgroup').length, 1);
|
||||||
|
});
|
||||||
|
|
@ -44,9 +44,9 @@ QUnit.test('init', function(assert) {
|
||||||
QUnit.test('filtering available options', function(assert) {
|
QUnit.test('filtering available options', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 3);
|
assert.equal($('#select_from option').length, 3);
|
||||||
assert.equal($('#select_to option').length, 0);
|
assert.equal($('#select_to option').length, 0);
|
||||||
|
|
@ -58,7 +58,7 @@ QUnit.test('filtering available options', function(assert) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
assert.equal($('#select_from option').length, 2);
|
assert.equal($('#select_from option').length, 2);
|
||||||
assert.equal($('#select_to option').length, 0);
|
assert.equal($('#select_to option').length, 0);
|
||||||
assert.equal($('#select_from option')[0].value, '1');
|
assert.equal($('#select_from option')[0].value, '2');
|
||||||
assert.equal($('#select_from option')[1].value, '3');
|
assert.equal($('#select_from option')[1].value, '3');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
@ -67,9 +67,9 @@ QUnit.test('filtering available options', function(assert) {
|
||||||
QUnit.test('filtering selected options', function(assert) {
|
QUnit.test('filtering selected options', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option selected value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option selected value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option selected value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option selected value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option selected value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option selected value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 0);
|
assert.equal($('#select_from option').length, 0);
|
||||||
assert.equal($('#select_to option').length, 3);
|
assert.equal($('#select_to option').length, 3);
|
||||||
|
|
@ -81,7 +81,7 @@ QUnit.test('filtering selected options', function(assert) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
assert.equal($('#select_from option').length, 0);
|
assert.equal($('#select_from option').length, 0);
|
||||||
assert.equal($('#select_to option').length, 2);
|
assert.equal($('#select_to option').length, 2);
|
||||||
assert.equal($('#select_to option')[0].value, '1');
|
assert.equal($('#select_to option')[0].value, '2');
|
||||||
assert.equal($('#select_to option')[1].value, '3');
|
assert.equal($('#select_to option')[1].value, '3');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
@ -90,9 +90,9 @@ QUnit.test('filtering selected options', function(assert) {
|
||||||
QUnit.test('filtering available options to nothing', function(assert) {
|
QUnit.test('filtering available options to nothing', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 3);
|
assert.equal($('#select_from option').length, 3);
|
||||||
assert.equal($('#select_to option').length, 0);
|
assert.equal($('#select_to option').length, 0);
|
||||||
|
|
@ -111,9 +111,9 @@ QUnit.test('filtering available options to nothing', function(assert) {
|
||||||
QUnit.test('filtering selected options to nothing', function(assert) {
|
QUnit.test('filtering selected options to nothing', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option selected value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option selected value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option selected value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option selected value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option selected value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option selected value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 0);
|
assert.equal($('#select_from option').length, 0);
|
||||||
assert.equal($('#select_to option').length, 3);
|
assert.equal($('#select_to option').length, 3);
|
||||||
|
|
@ -132,9 +132,9 @@ QUnit.test('filtering selected options to nothing', function(assert) {
|
||||||
QUnit.test('selecting option', function(assert) {
|
QUnit.test('selecting option', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 3);
|
assert.equal($('#select_from option').length, 3);
|
||||||
assert.equal($('#select_to option').length, 0);
|
assert.equal($('#select_to option').length, 0);
|
||||||
|
|
@ -154,13 +154,13 @@ QUnit.test('selecting option', function(assert) {
|
||||||
QUnit.test('deselecting option', function(assert) {
|
QUnit.test('deselecting option', function(assert) {
|
||||||
const $ = django.jQuery;
|
const $ = django.jQuery;
|
||||||
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
$('<form><select multiple id="select"></select></form>').appendTo('#qunit-fixture');
|
||||||
$('<option selected value="1" title="Red">Red</option>').appendTo('#select');
|
$('<option value="1" title="Blue">Blue</option>').appendTo('#select');
|
||||||
$('<option value="2" title="Blue">Blue</option>').appendTo('#select');
|
$('<option value="2" title="Green">Green</option>').appendTo('#select');
|
||||||
$('<option value="3" title="Green">Green</option>').appendTo('#select');
|
$('<option selected value="3" title="Red">Red</option>').appendTo('#select');
|
||||||
SelectFilter.init('select', 'items', 0);
|
SelectFilter.init('select', 'items', 0);
|
||||||
assert.equal($('#select_from option').length, 2);
|
assert.equal($('#select_from option').length, 2);
|
||||||
assert.equal($('#select_to option').length, 1);
|
assert.equal($('#select_to option').length, 1);
|
||||||
assert.equal($('#select_to option')[0].value, '1');
|
assert.equal($('#select_to option')[0].value, '3');
|
||||||
// move back to the left
|
// move back to the left
|
||||||
const done_left = assert.async();
|
const done_left = assert.async();
|
||||||
$('#select_to')[0].selectedIndex = 0;
|
$('#select_to')[0].selectedIndex = 0;
|
||||||
|
|
|
||||||
|
|
@ -957,6 +957,7 @@ class RelatedFieldWidgetWrapperTests(SimpleTestCase):
|
||||||
self.assertIn("<a ", output)
|
self.assertIn("<a ", output)
|
||||||
|
|
||||||
def test_data_model_ref_when_model_name_is_camel_case(self):
|
def test_data_model_ref_when_model_name_is_camel_case(self):
|
||||||
|
self.maxDiff = None
|
||||||
rel = VideoStream._meta.get_field("release_event").remote_field
|
rel = VideoStream._meta.get_field("release_event").remote_field
|
||||||
widget = forms.Select()
|
widget = forms.Select()
|
||||||
wrapper = widgets.RelatedFieldWidgetWrapper(widget, rel, widget_admin_site)
|
wrapper = widgets.RelatedFieldWidgetWrapper(widget, rel, widget_admin_site)
|
||||||
|
|
@ -971,7 +972,7 @@ class RelatedFieldWidgetWrapperTests(SimpleTestCase):
|
||||||
</select>
|
</select>
|
||||||
<a class="related-widget-wrapper-link add-related" id="add_id_stream"
|
<a class="related-widget-wrapper-link add-related" id="add_id_stream"
|
||||||
data-popup="yes" title="Add another release event"
|
data-popup="yes" title="Add another release event"
|
||||||
href="/admin_widgets/releaseevent/add/?_to_field=album&_popup=1">
|
href="/admin_widgets/releaseevent/add/?_to_field=album&_popup=1&_source_model=admin_widgets.videostream">
|
||||||
<img src="/static/admin/img/icon-addlink.svg" alt="" width="24" height="24">
|
<img src="/static/admin/img/icon-addlink.svg" alt="" width="24" height="24">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1354,14 +1355,14 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box,
|
to_box,
|
||||||
[
|
[
|
||||||
str(self.lisa.id),
|
|
||||||
str(self.peter.id),
|
|
||||||
str(self.arthur.id),
|
str(self.arthur.id),
|
||||||
str(self.bob.id),
|
str(self.bob.id),
|
||||||
str(self.cliff.id),
|
str(self.cliff.id),
|
||||||
str(self.jason.id),
|
str(self.jason.id),
|
||||||
str(self.jenny.id),
|
str(self.jenny.id),
|
||||||
str(self.john.id),
|
str(self.john.id),
|
||||||
|
str(self.lisa.id),
|
||||||
|
str(self.peter.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertButtonsDisabled(
|
self.assertButtonsDisabled(
|
||||||
|
|
@ -1387,14 +1388,14 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
from_box,
|
from_box,
|
||||||
[
|
[
|
||||||
str(self.lisa.id),
|
|
||||||
str(self.peter.id),
|
|
||||||
str(self.arthur.id),
|
str(self.arthur.id),
|
||||||
str(self.bob.id),
|
str(self.bob.id),
|
||||||
str(self.cliff.id),
|
str(self.cliff.id),
|
||||||
str(self.jason.id),
|
str(self.jason.id),
|
||||||
str(self.jenny.id),
|
str(self.jenny.id),
|
||||||
str(self.john.id),
|
str(self.john.id),
|
||||||
|
str(self.lisa.id),
|
||||||
|
str(self.peter.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(to_box, [])
|
self.assertSelectOptions(to_box, [])
|
||||||
|
|
@ -1443,19 +1444,19 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
from_box,
|
from_box,
|
||||||
[
|
[
|
||||||
str(self.peter.id),
|
|
||||||
str(self.arthur.id),
|
str(self.arthur.id),
|
||||||
str(self.cliff.id),
|
str(self.cliff.id),
|
||||||
str(self.jenny.id),
|
str(self.jenny.id),
|
||||||
|
str(self.peter.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box,
|
to_box,
|
||||||
[
|
[
|
||||||
str(self.lisa.id),
|
|
||||||
str(self.bob.id),
|
str(self.bob.id),
|
||||||
str(self.jason.id),
|
str(self.jason.id),
|
||||||
str(self.john.id),
|
str(self.john.id),
|
||||||
|
str(self.lisa.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1492,12 +1493,12 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
from_box,
|
from_box,
|
||||||
[
|
[
|
||||||
str(self.peter.id),
|
|
||||||
str(self.arthur.id),
|
str(self.arthur.id),
|
||||||
|
str(self.bob.id),
|
||||||
str(self.cliff.id),
|
str(self.cliff.id),
|
||||||
str(self.jenny.id),
|
str(self.jenny.id),
|
||||||
str(self.lisa.id),
|
str(self.lisa.id),
|
||||||
str(self.bob.id),
|
str(self.peter.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(to_box, [str(self.jason.id), str(self.john.id)])
|
self.assertSelectOptions(to_box, [str(self.jason.id), str(self.john.id)])
|
||||||
|
|
@ -1510,19 +1511,19 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
from_box,
|
from_box,
|
||||||
[
|
[
|
||||||
str(self.peter.id),
|
str(self.bob.id),
|
||||||
str(self.jenny.id),
|
str(self.jenny.id),
|
||||||
str(self.lisa.id),
|
str(self.lisa.id),
|
||||||
str(self.bob.id),
|
str(self.peter.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box,
|
to_box,
|
||||||
[
|
[
|
||||||
str(self.jason.id),
|
|
||||||
str(self.john.id),
|
|
||||||
str(self.arthur.id),
|
str(self.arthur.id),
|
||||||
str(self.cliff.id),
|
str(self.cliff.id),
|
||||||
|
str(self.jason.id),
|
||||||
|
str(self.john.id),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1532,9 +1533,9 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
|
|
||||||
# Confirm they're selected after clicking inactive buttons: ticket
|
# Confirm they're selected after clicking inactive buttons: ticket
|
||||||
# #26575
|
# #26575
|
||||||
self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)])
|
self.assertSelectedOptions(from_box, [str(self.lisa.id), str(self.peter.id)])
|
||||||
self.selenium.find_element(By.ID, remove_button).click()
|
self.selenium.find_element(By.ID, remove_button).click()
|
||||||
self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)])
|
self.assertSelectedOptions(from_box, [str(self.lisa.id), str(self.peter.id)])
|
||||||
|
|
||||||
# Unselect the options ------------------------------------------------
|
# Unselect the options ------------------------------------------------
|
||||||
self.deselect_option(from_box, str(self.peter.id))
|
self.deselect_option(from_box, str(self.peter.id))
|
||||||
|
|
@ -1661,9 +1662,9 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box,
|
to_box,
|
||||||
[
|
[
|
||||||
|
str(self.jason.id),
|
||||||
str(self.lisa.id),
|
str(self.lisa.id),
|
||||||
str(self.peter.id),
|
str(self.peter.id),
|
||||||
str(self.jason.id),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1673,7 +1674,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
from_box, [str(self.arthur.id), str(self.lisa.id)]
|
from_box, [str(self.arthur.id), str(self.lisa.id)]
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box, [str(self.peter.id), str(self.jason.id)]
|
to_box, [str(self.jason.id), str(self.peter.id)]
|
||||||
)
|
)
|
||||||
|
|
||||||
input.send_keys([Keys.BACK_SPACE]) # Clear text box
|
input.send_keys([Keys.BACK_SPACE]) # Clear text box
|
||||||
|
|
@ -1689,7 +1690,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box, [str(self.peter.id), str(self.jason.id)]
|
to_box, [str(self.jason.id), str(self.peter.id)]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Pressing enter on a filtered option sends it properly to
|
# Pressing enter on a filtered option sends it properly to
|
||||||
|
|
@ -1700,7 +1701,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
|
||||||
self.assertSelectOptions(from_box, [str(self.jason.id)])
|
self.assertSelectOptions(from_box, [str(self.jason.id)])
|
||||||
input.send_keys([Keys.ENTER])
|
input.send_keys([Keys.ENTER])
|
||||||
self.assertSelectOptions(
|
self.assertSelectOptions(
|
||||||
to_box, [str(self.peter.id), str(self.jason.id)]
|
to_box, [str(self.jason.id), str(self.peter.id)]
|
||||||
)
|
)
|
||||||
input.send_keys([Keys.BACK_SPACE, Keys.BACK_SPACE])
|
input.send_keys([Keys.BACK_SPACE, Keys.BACK_SPACE])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue