Skip to content

Commit 2625814

Browse files
committed
Merge branch 'release/1.5.0'
2 parents b93f459 + 81b6cab commit 2625814

27 files changed

+529
-173
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ htmlcov
66
.coverage
77
docs/_build
88
example/ajax_select
9-
example/ajax_selects_example
9+
example/ajax_selects_example_db
1010
dist
1111
MANIFEST

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ sudo: false
33
env:
44
- TOX_ENV=py27-flake8
55
- TOX_ENV=py34-flake8
6-
- TOX_ENV=py27-dj15
76
- TOX_ENV=py27-dj16
8-
- TOX_ENV=py33-dj15
97
- TOX_ENV=py33-dj16
108
- TOX_ENV=py27-dj17
119
- TOX_ENV=py27-dj18
1210
- TOX_ENV=py27-dj19
11+
- TOX_ENV=py27-dj110
1312
- TOX_ENV=py34-dj17
1413
- TOX_ENV=py34-dj18
1514
- TOX_ENV=py34-dj19
15+
- TOX_ENV=py34-dj110
1616
# - TOX_ENV=py35-dj18
1717
# - TOX_ENV=py35-dj19
1818
install:

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Change Log
22

3+
## [1.5.0](https://github.com/crucialfelix/django-ajax-selects/tree/1.5.0) (2016-09-05)
4+
[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.4.3...1.5.0)
5+
6+
- Added Support for Django 1.10
7+
- Dropped Django 1.5
8+
9+
**Closed issues:**
10+
11+
- ValueError in Django 1.10 [\#177](https://github.com/crucialfelix/django-ajax-selects/issues/177)
12+
- Django 1.10 did add popup [\#174](https://github.com/crucialfelix/django-ajax-selects/issues/174)
13+
- Example not Working [\#161](https://github.com/crucialfelix/django-ajax-selects/issues/161)
14+
15+
**Merged pull requests:**
16+
17+
- Fix documentation to format code properly [\#165](https://github.com/crucialfelix/django-ajax-selects/pull/165) ([joshblum](https://github.com/joshblum))
18+
- install.sh not working [\#162](https://github.com/crucialfelix/django-ajax-selects/pull/162) ([hdzierz](https://github.com/hdzierz))
19+
320
## [1.4.3](https://github.com/crucialfelix/django-ajax-selects/tree/1.4.3) (2016-03-13)
421
[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.4.2...1.4.3)
522

@@ -214,4 +231,4 @@
214231
## [1.1.0](https://github.com/crucialfelix/django-ajax-selects/tree/1.1.0) (2010-03-06)
215232

216233

217-
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
234+
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Assets included by default
7676
Compatibility
7777
-------------
7878

79-
- Django >=1.5, <=1.9
79+
- Django >=1.6, <=1.10
8080
- Python >=2.7, 3.3-3.5
8181

8282

ajax_select/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from ajax_select.helpers import make_ajax_form, make_ajax_field # noqa
99
from ajax_select.lookup_channel import LookupChannel # noqa
1010

11-
1211
try:
1312
# django 1.7+ will use the new AppConfig api
1413
# It will load all your lookups.py modules

ajax_select/fields.py

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import unicode_literals
2+
import json
23
from ajax_select.registry import registry
34
from django import forms
45
from django.conf import settings
56
from django.contrib.contenttypes.models import ContentType
67
from django.core.urlresolvers import reverse
8+
from django.db.models.query import QuerySet
79
try:
810
from django.forms.utils import flatatt
911
except ImportError:
@@ -15,7 +17,6 @@
1517
from django.utils.safestring import mark_safe
1618
from django.utils.six import text_type
1719
from django.utils.translation import ugettext as _
18-
import json
1920

2021

2122
as_default_help = 'Enter text to search.'
@@ -49,10 +50,10 @@ def __init__(self,
4950
channel,
5051
help_text='',
5152
show_help_text=True,
52-
plugin_options={},
53+
plugin_options=None,
5354
*args,
5455
**kwargs):
55-
self.plugin_options = plugin_options
56+
self.plugin_options = plugin_options or {}
5657
super(forms.widgets.TextInput, self).__init__(*args, **kwargs)
5758
self.channel = channel
5859
self.help_text = help_text
@@ -90,9 +91,10 @@ def render(self, name, value, attrs=None):
9091
'func_slug': self.html_id.replace("-", ""),
9192
'add_link': self.add_link,
9293
}
93-
context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
94-
templates = ('ajax_select/autocompleteselect_%s.html' % self.channel,
95-
'ajax_select/autocompleteselect.html')
94+
context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
95+
templates = (
96+
'ajax_select/autocompleteselect_%s.html' % self.channel,
97+
'ajax_select/autocompleteselect.html')
9698
out = render_to_string(templates, context)
9799
return mark_safe(out)
98100

@@ -162,15 +164,15 @@ def __init__(self,
162164
channel,
163165
help_text='',
164166
show_help_text=True,
165-
plugin_options={},
167+
plugin_options=None,
166168
*args,
167169
**kwargs):
168170
super(AutoCompleteSelectMultipleWidget, self).__init__(*args, **kwargs)
169171
self.channel = channel
170172

171173
self.help_text = help_text
172174
self.show_help_text = show_help_text
173-
self.plugin_options = plugin_options
175+
self.plugin_options = plugin_options or {}
174176

175177
def render(self, name, value, attrs=None):
176178

@@ -182,20 +184,18 @@ def render(self, name, value, attrs=None):
182184

183185
lookup = registry.get(self.channel)
184186

185-
# eg. value = [3002L, 1194L]
186-
if value:
187-
# |pk|pk| of current
188-
current_ids = "|" + "|".join(str(pk) for pk in value) + "|"
187+
if isinstance(value, QuerySet):
188+
objects = value
189189
else:
190-
current_ids = "|"
190+
objects = lookup.get_objects(value)
191191

192-
objects = lookup.get_objects(value)
192+
current_ids = pack_ids([obj.pk for obj in objects])
193193

194194
# text repr of currently selected items
195-
initial = []
196-
for obj in objects:
197-
display = lookup.format_item_display(obj)
198-
initial.append([display, obj.pk])
195+
initial = [
196+
[lookup.format_item_display(obj), obj.pk]
197+
for obj in objects
198+
]
199199

200200
if self.show_help_text:
201201
help_text = self.help_text
@@ -213,7 +213,7 @@ def render(self, name, value, attrs=None):
213213
'func_slug': self.html_id.replace("-", ""),
214214
'add_link': self.add_link,
215215
}
216-
context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
216+
context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
217217
templates = ('ajax_select/autocompleteselectmultiple_%s.html' % self.channel,
218218
'ajax_select/autocompleteselectmultiple.html')
219219
out = render_to_string(templates, context)
@@ -344,7 +344,7 @@ def render(self, name, value, attrs=None):
344344
'extra_attrs': mark_safe(flatatt(final_attrs)),
345345
'func_slug': self.html_id.replace("-", ""),
346346
}
347-
context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
347+
context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
348348
templates = ('ajax_select/autocomplete_%s.html' % self.channel,
349349
'ajax_select/autocomplete.html')
350350
return mark_safe(render_to_string(templates, context))
@@ -394,10 +394,9 @@ def _check_can_add(self, user, related_model):
394394
ctype = ContentType.objects.get_for_model(related_model)
395395
can_add = user.has_perm("%s.add_%s" % (ctype.app_label, ctype.model))
396396
if can_add:
397-
self.widget.add_link = reverse('add_popup', kwargs={
398-
'app_label': related_model._meta.app_label,
399-
'model': related_model._meta.object_name.lower()
400-
})
397+
app_label = related_model._meta.app_label
398+
model = related_model._meta.object_name.lower()
399+
self.widget.add_link = reverse('admin:%s_%s_add' % (app_label, model)) + '?_popup=1'
401400

402401

403402
def autoselect_fields_check_can_add(form, model, user):
@@ -411,7 +410,7 @@ def autoselect_fields_check_can_add(form, model, user):
411410
form_field.check_can_add(user, db_field.rel.to)
412411

413412

414-
def plugin_options(lookup, channel_name, widget_plugin_options, initial):
413+
def make_plugin_options(lookup, channel_name, widget_plugin_options, initial):
415414
""" Make a JSON dumped dict of all options for the jQuery ui plugin."""
416415
po = {}
417416
if initial:
@@ -429,3 +428,11 @@ def plugin_options(lookup, channel_name, widget_plugin_options, initial):
429428
'plugin_options': mark_safe(json.dumps(po)),
430429
'data_plugin_options': force_escape(json.dumps(po))
431430
}
431+
432+
433+
def pack_ids(ids):
434+
if ids:
435+
# |pk|pk| of current
436+
return "|" + "|".join(str(pk) for pk in ids) + "|"
437+
else:
438+
return "|"

ajax_select/registry.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class LookupChannelRegistry(object):
1313
_registry = {}
1414

1515
def load_channels(self):
16+
"""
17+
Called when loading the application. Cannot be called a second time,
18+
(eg. for testing) as Django will not re-import and re-register anything.
19+
"""
1620
self._registry = {}
1721
try:
1822
from django.utils.module_loading import autodiscover_modules

ajax_select/static/ajax_select/js/ajax_select.js

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
'use strict';
21

32
(function($) {
3+
'use strict';
44

55
$.fn.autocompleteselect = function(options) {
66
return this.each(function() {
@@ -134,26 +134,20 @@
134134

135135
function addAutoComplete (inp, callback) {
136136
var $inp = $(inp),
137-
html_id = inp.id,
138-
prefix_id = html_id,
139-
opts = JSON.parse($inp.attr('data-plugin-options')),
140-
prefix = 0;
141-
142-
/* detects inline forms and converts the html_id if needed */
143-
if (html_id.indexOf('__prefix__') !== -1) {
144-
// Some dirty loop to find the appropriate element to apply the callback to
145-
while ($('#' + html_id).length) {
146-
html_id = prefix_id.replace(/__prefix__/, prefix++);
147-
}
148-
html_id = prefix_id.replace(/__prefix__/, prefix - 2);
149-
// Ignore the first call to this function, the one that is triggered when
150-
// page is loaded just because the 'empty' form is there.
151-
if ($('#' + html_id + ', #' + html_id + '_text').hasClass('ui-autocomplete-input')) {
152-
return;
153-
}
137+
opts = JSON.parse($inp.attr('data-plugin-options'));
138+
// Do not activate empty-form inline rows.
139+
// These are cloned into the form when adding another row and will be activated at that time.
140+
if ($inp.attr('id').indexOf('__prefix__') !== -1) {
141+
// console.log('skipping __prefix__ row', $inp);
142+
return;
154143
}
155-
144+
if ($inp.data('_ajax_select_inited_')) {
145+
// console.log('skipping already activated row', $inp);
146+
return;
147+
}
148+
// console.log('activating', $inp);
156149
callback($inp, opts);
150+
$inp.data('_ajax_select_inited_', true);
157151
}
158152

159153
// allow html in the results menu
@@ -187,14 +181,28 @@
187181
}
188182
});
189183

190-
/* the popup handler
191-
requires RelatedObjects.js which is part of the django admin js
192-
so if using outside of the admin then you would need to include that manually */
193-
window.didAddPopup = function (win, newId, newRepr) {
184+
/* Called by the popup create object when it closes.
185+
* For the popup this is opener.dismissAddRelatedObjectPopup
186+
* Django implements this in RelatedObjectLookups.js
187+
*/
188+
var djangoDismissAddRelatedObjectPopup = window.dismissAddRelatedObjectPopup || window.dismissAddAnotherPopup;
189+
window.dismissAddRelatedObjectPopup = function(win, newId, newRepr) {
190+
// This may be called for ajax-select inputs or for other inputs.
191+
// Call the original which sets the input (just the pk)
192+
// calls input.trigger('changed') if >= 1.10
193+
// and closes the window.
194+
if (djangoDismissAddRelatedObjectPopup) {
195+
djangoDismissAddRelatedObjectPopup(win, newId, newRepr);
196+
} else {
197+
win.close();
198+
}
194199
var name = window.windowname_to_id(win.name);
195-
$('#' + name).trigger('didAddPopup', [window.html_unescape(newId), window.html_unescape(newRepr)]);
196-
win.close();
197-
};
200+
// newRepr is django's repr of object
201+
// not the Lookup's formatting of it.
202+
$('#' + name).trigger('didAddPopup', [newId, newRepr]);
203+
}
204+
// Django renamed this function in 1.8
205+
window.dismissAddAnotherPopup = window.dismissAddRelatedObjectPopup;
198206

199207
// activate any on page
200208
$(window).bind('init-autocomplete', function() {
@@ -228,6 +236,7 @@
228236
// if dynamically injecting forms onto a page
229237
// you can trigger them to be ajax-selects-ified:
230238
$(window).trigger('init-autocomplete');
239+
// When adding new rows in inline forms, reinitialize and activate newly added rows.
231240
$(document)
232241
.on('click', '.inline-group ul.tools a.add, .inline-group div.add-row a, .inline-group .tabular tr.add-row td a', function() {
233242
$(window).trigger('init-autocomplete');

ajax_select/urls.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,5 @@
44
urlpatterns = [
55
url(r'^ajax_lookup/(?P<channel>[-\w]+)$',
66
views.ajax_lookup,
7-
name='ajax_lookup'),
8-
url(r'^add_popup/(?P<app_label>\w+)/(?P<model>\w+)$',
9-
views.add_popup,
10-
name='add_popup')
7+
name='ajax_lookup')
118
]

0 commit comments

Comments
 (0)