[Python-modules-commits] [django-ajax-selects] 02/08: New upstream version 1.6.0
Brian May
bam at moszumanska.debian.org
Wed Jul 5 07:39:54 UTC 2017
This is an automated email from the git hooks/post-receive script.
bam pushed a commit to branch debian/master
in repository django-ajax-selects.
commit 4a4c9864d0a2844edb4e2f7656fe8165b37c3910
Author: Brian May <bam at debian.org>
Date: Wed Jul 5 17:13:52 2017 +1000
New upstream version 1.6.0
---
CHANGELOG.md | 59 +++++++-
PKG-INFO | 6 +-
README.md | 2 +-
ajax_select/__init__.py | 3 +-
ajax_select/fields.py | 123 +++++++++------
ajax_select/lookup_channel.py | 58 ++++---
ajax_select/registry.py | 4 +
ajax_select/static/ajax_select/js/ajax_select.js | 67 +++++----
ajax_select/static/ajax_select/js/bootstrap.js | 37 ++++-
ajax_select/urls.py | 5 +-
ajax_select/views.py | 62 +-------
django_ajax_selects.egg-info/PKG-INFO | 6 +-
django_ajax_selects.egg-info/SOURCES.txt | 2 +
requirements-test.txt | 10 +-
requirements.txt | 4 +-
setup.py | 6 +-
tests/admin.py | 29 +++-
tests/lookups.py | 20 ++-
tests/models.py | 3 +-
tests/test_fields.py | 24 +++
tests/test_integration.py | 184 +++++++++++++++++++++++
tests/test_lookups.py | 21 +++
tests/test_registry.py | 7 +-
tests/test_views.py | 28 ----
24 files changed, 542 insertions(+), 228 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e59478..40e0a0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,62 @@
# Change Log
+## [1.6.0](https://github.com/crucialfelix/django-ajax-selects/tree/1.6.0) (2017-05-17)
+[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.5.2...1.6.0)
+
+Add support for Django 1.11
+Drop support for Django 1.6
+
+**Closed issues:**
+
+- LookupChannel.get\_objects fails for inherited models [\#153](https://github.com/crucialfelix/django-ajax-selects/issues/153)
+
+**Merged pull requests:**
+
+- Changed the build\_attrs to work with Django==1.11. [\#202](https://github.com/crucialfelix/django-ajax-selects/pull/202) ([xbello](https://github.com/xbello))
+
+## [1.5.2](https://github.com/crucialfelix/django-ajax-selects/tree/1.5.2) (2016-10-19)
+[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.5.1...1.5.2)
+
+**Fixed bugs:**
+
+- Occasionally: $.ui.autocomplete is undefined [\#188](https://github.com/crucialfelix/django-ajax-selects/issues/188)
+
+**Closed issues:**
+
+- No cache management headers in HTTP response [\#187](https://github.com/crucialfelix/django-ajax-selects/issues/187)
+
+## [1.5.1](https://github.com/crucialfelix/django-ajax-selects/tree/1.5.1) (2016-10-13)
+[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.5.0...1.5.1)
+
+**Implemented enhancements:**
+
+- Prefer document.createElement to document.write [\#182](https://github.com/crucialfelix/django-ajax-selects/issues/182)
+
+**Fixed bugs:**
+
+- fix: add related for multiple select [\#184](https://github.com/crucialfelix/django-ajax-selects/pull/184) ([crucialfelix](https://github.com/crucialfelix))
+
+## [1.5.0](https://github.com/crucialfelix/django-ajax-selects/tree/1.5.0) (2016-09-05)
+[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.4.3...1.5.0)
+
+- Added Support for Django 1.10
+- Dropped Django 1.5
+
+**Fixed bugs:**
+
+- Initial fields are duplicated when new row added. [\#94](https://github.com/crucialfelix/django-ajax-selects/issues/94)
+
+**Closed issues:**
+
+- ValueError in Django 1.10 [\#177](https://github.com/crucialfelix/django-ajax-selects/issues/177)
+- Django 1.10 did add popup [\#174](https://github.com/crucialfelix/django-ajax-selects/issues/174)
+- Example not Working [\#161](https://github.com/crucialfelix/django-ajax-selects/issues/161)
+
+**Merged pull requests:**
+
+- Fix documentation to format code properly [\#165](https://github.com/crucialfelix/django-ajax-selects/pull/165) ([joshblum](https://github.com/joshblum))
+- install.sh not working [\#162](https://github.com/crucialfelix/django-ajax-selects/pull/162) ([hdzierz](https://github.com/hdzierz))
+
## [1.4.3](https://github.com/crucialfelix/django-ajax-selects/tree/1.4.3) (2016-03-13)
[Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.4.2...1.4.3)
@@ -214,4 +271,4 @@
## [1.1.0](https://github.com/crucialfelix/django-ajax-selects/tree/1.1.0) (2010-03-06)
-\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\ No newline at end of file
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/PKG-INFO b/PKG-INFO
index ee5b2b1..0a15fe9 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-ajax-selects
-Version: 1.4.3
+Version: 1.6.0
Summary: Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.
Home-page: https://github.com/crucialfelix/django-ajax-selects/
Author: Chris Sattinger
@@ -17,8 +17,8 @@ Description: Edit ForeignKey, ManyToManyField and CharField in Django Admin usin
- Integrate with other UI elements elsewhere on the page using the javascript API
- Works in Admin as well as in normal views
- - Django >=1.5, <=1.9
- - Python >=2.7, <=3.5
+ - Django >=1.7, <=2
+ - Python >=2.7, <=3.7
Platform: UNKNOWN
Classifier: Programming Language :: Python
diff --git a/README.md b/README.md
index 4090717..034fb73 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ Assets included by default
Compatibility
-------------
-- Django >=1.5, <=1.9
+- Django >=1.6, <=1.10
- Python >=2.7, 3.3-3.5
diff --git a/ajax_select/__init__.py b/ajax_select/__init__.py
index aa11ce2..a4381dc 100644
--- a/ajax_select/__init__.py
+++ b/ajax_select/__init__.py
@@ -1,5 +1,5 @@
"""JQuery-Ajax Autocomplete fields for Django Forms."""
-__version__ = "1.4.3"
+__version__ = "1.6.0"
__author__ = "crucialfelix"
__contact__ = "crucialfelix at gmail.com"
__homepage__ = "https://github.com/crucialfelix/django-ajax-selects/"
@@ -8,7 +8,6 @@ from ajax_select.registry import registry, register # noqa
from ajax_select.helpers import make_ajax_form, make_ajax_field # noqa
from ajax_select.lookup_channel import LookupChannel # noqa
-
try:
# django 1.7+ will use the new AppConfig api
# It will load all your lookups.py modules
diff --git a/ajax_select/fields.py b/ajax_select/fields.py
index 15edda3..2692c45 100644
--- a/ajax_select/fields.py
+++ b/ajax_select/fields.py
@@ -1,21 +1,22 @@
from __future__ import unicode_literals
+import json
from ajax_select.registry import registry
from django import forms
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
-try:
- from django.forms.utils import flatatt
-except ImportError:
- # < django 1.7
- from django.forms.util import flatatt
-from django.template.loader import render_to_string
+from django.db.models.query import QuerySet
+from django.forms.utils import flatatt
from django.template.defaultfilters import force_escape
+from django.template.loader import render_to_string
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.six import text_type
from django.utils.translation import ugettext as _
-import json
+try:
+ from django.urls import reverse
+except ImportError:
+ # < django 1.10
+ from django.core.urlresolvers import reverse
as_default_help = 'Enter text to search.'
@@ -34,12 +35,14 @@ def _media(self):
return forms.Media(css={'all': ('ajax_select/css/ajax_select.css',)}, js=js)
-####################################################################################
+###############################################################################
class AutoCompleteSelectWidget(forms.widgets.TextInput):
- """Widget to search for a model and return it as text for use in a CharField."""
+ """
+ Widget to search for a model and return it as text for use in a CharField.
+ """
media = property(_media)
@@ -49,10 +52,10 @@ class AutoCompleteSelectWidget(forms.widgets.TextInput):
channel,
help_text='',
show_help_text=True,
- plugin_options={},
+ plugin_options=None,
*args,
**kwargs):
- self.plugin_options = plugin_options
+ self.plugin_options = plugin_options or {}
super(forms.widgets.TextInput, self).__init__(*args, **kwargs)
self.channel = channel
self.help_text = help_text
@@ -60,7 +63,10 @@ class AutoCompleteSelectWidget(forms.widgets.TextInput):
def render(self, name, value, attrs=None):
value = value or ''
- final_attrs = self.build_attrs(attrs)
+
+ final_attrs = self.build_attrs(self.attrs)
+ final_attrs.update(attrs or {})
+ final_attrs.pop('required', None)
self.html_id = final_attrs.pop('id', name)
current_repr = ''
@@ -90,9 +96,10 @@ class AutoCompleteSelectWidget(forms.widgets.TextInput):
'func_slug': self.html_id.replace("-", ""),
'add_link': self.add_link,
}
- context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
- templates = ('ajax_select/autocompleteselect_%s.html' % self.channel,
- 'ajax_select/autocompleteselect.html')
+ context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
+ templates = (
+ 'ajax_select/autocompleteselect_%s.html' % self.channel,
+ 'ajax_select/autocompleteselect.html')
out = render_to_string(templates, context)
return mark_safe(out)
@@ -129,7 +136,8 @@ class AutoCompleteSelectField(forms.fields.CharField):
if len(objs) != 1:
# someone else might have deleted it while you were editing
# or your channel is faulty
- # out of the scope of this field to do anything more than tell you it doesn't exist
+ # out of the scope of this field to do anything more than
+ # tell you it doesn't exist
raise forms.ValidationError("%s cannot find object: %s" % (lookup, value))
return objs[0]
else:
@@ -147,12 +155,14 @@ class AutoCompleteSelectField(forms.fields.CharField):
return text_type(initial_value) != text_type(data_value)
-####################################################################################
+###############################################################################
class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple):
- """Widget to select multiple models for a ManyToMany db field."""
+ """
+ Widget to select multiple models for a ManyToMany db field.
+ """
media = property(_media)
@@ -162,7 +172,7 @@ class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple):
channel,
help_text='',
show_help_text=True,
- plugin_options={},
+ plugin_options=None,
*args,
**kwargs):
super(AutoCompleteSelectMultipleWidget, self).__init__(*args, **kwargs)
@@ -170,32 +180,32 @@ class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple):
self.help_text = help_text
self.show_help_text = show_help_text
- self.plugin_options = plugin_options
+ self.plugin_options = plugin_options or {}
def render(self, name, value, attrs=None):
if value is None:
value = []
- final_attrs = self.build_attrs(attrs)
+ final_attrs = self.build_attrs(self.attrs)
+ final_attrs.update(attrs or {})
+ final_attrs.pop('required', None)
self.html_id = final_attrs.pop('id', name)
lookup = registry.get(self.channel)
- # eg. value = [3002L, 1194L]
- if value:
- # |pk|pk| of current
- current_ids = "|" + "|".join(str(pk) for pk in value) + "|"
+ if isinstance(value, QuerySet):
+ objects = value
else:
- current_ids = "|"
+ objects = lookup.get_objects(value)
- objects = lookup.get_objects(value)
+ current_ids = pack_ids([obj.pk for obj in objects])
# text repr of currently selected items
- initial = []
- for obj in objects:
- display = lookup.format_item_display(obj)
- initial.append([display, obj.pk])
+ initial = [
+ [lookup.format_item_display(obj), obj.pk]
+ for obj in objects
+ ]
if self.show_help_text:
help_text = self.help_text
@@ -213,7 +223,7 @@ class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple):
'func_slug': self.html_id.replace("-", ""),
'add_link': self.add_link,
}
- context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
+ context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
templates = ('ajax_select/autocompleteselectmultiple_%s.html' % self.channel,
'ajax_select/autocompleteselectmultiple.html')
out = render_to_string(templates, context)
@@ -229,7 +239,9 @@ class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple):
class AutoCompleteSelectMultipleField(forms.fields.CharField):
- """ form field to select multiple models for a ManyToMany db field """
+ """
+ Form field to select multiple models for a ManyToMany db field.
+ """
channel = None
@@ -245,8 +257,8 @@ class AutoCompleteSelectMultipleField(forms.fields.CharField):
if isinstance(help_text, str):
help_text = force_text(help_text)
# django admin appends "Hold down "Control",..." to the help text
- # regardless of which widget is used. so even when you specify an explicit
- # help text it appends this other default text onto the end.
+ # regardless of which widget is used. so even when you specify an
+ # explicit help text it appends this other default text onto the end.
# This monkey patches the help text to remove that
if help_text != '':
if not isinstance(help_text, text_type):
@@ -298,14 +310,15 @@ class AutoCompleteSelectMultipleField(forms.fields.CharField):
dvs = [text_type(v) for v in (data_value or [])]
return ivs != dvs
-####################################################################################
+###############################################################################
class AutoCompleteWidget(forms.TextInput):
"""
- Widget to select a search result and enter the result as raw text in the text input field.
- the user may also simply enter text and ignore any auto complete suggestions.
+ Widget to select a search result and enter the result as raw text in the
+ text input field. The user may also simply enter text and ignore any
+ auto complete suggestions.
"""
media = property(_media)
@@ -325,9 +338,10 @@ class AutoCompleteWidget(forms.TextInput):
def render(self, name, value, attrs=None):
initial = value or ''
-
- final_attrs = self.build_attrs(attrs)
+ final_attrs = self.build_attrs(self.attrs)
+ final_attrs.update(attrs or {})
self.html_id = final_attrs.pop('id', name)
+ final_attrs.pop('required', None)
lookup = registry.get(self.channel)
if self.show_help_text:
@@ -344,7 +358,7 @@ class AutoCompleteWidget(forms.TextInput):
'extra_attrs': mark_safe(flatatt(final_attrs)),
'func_slug': self.html_id.replace("-", ""),
}
- context.update(plugin_options(lookup, self.channel, self.plugin_options, initial))
+ context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
templates = ('ajax_select/autocomplete_%s.html' % self.channel,
'ajax_select/autocomplete.html')
return mark_safe(render_to_string(templates, context))
@@ -352,7 +366,8 @@ class AutoCompleteWidget(forms.TextInput):
class AutoCompleteField(forms.CharField):
"""
- A CharField that uses an AutoCompleteWidget to lookup matching and stores the result as plain text.
+ A CharField that uses an AutoCompleteWidget to lookup matching
+ and stores the result as plain text.
"""
channel = None
@@ -375,7 +390,7 @@ class AutoCompleteField(forms.CharField):
super(AutoCompleteField, self).__init__(*args, **defaults)
-####################################################################################
+###############################################################################
def _check_can_add(self, user, related_model):
"""
@@ -394,16 +409,16 @@ def _check_can_add(self, user, related_model):
ctype = ContentType.objects.get_for_model(related_model)
can_add = user.has_perm("%s.add_%s" % (ctype.app_label, ctype.model))
if can_add:
- self.widget.add_link = reverse('add_popup', kwargs={
- 'app_label': related_model._meta.app_label,
- 'model': related_model._meta.object_name.lower()
- })
+ app_label = related_model._meta.app_label
+ model = related_model._meta.object_name.lower()
+ self.widget.add_link = reverse('admin:%s_%s_add' % (app_label, model)) + '?_popup=1'
def autoselect_fields_check_can_add(form, model, user):
"""
Check the form's fields for any autoselect fields and enable their
- widgets with green + button if permissions allow then to create the related_model.
+ widgets with green + button if permissions allow then to create the
+ related_model.
"""
for name, form_field in form.declared_fields.items():
if isinstance(form_field, (AutoCompleteSelectMultipleField, AutoCompleteSelectField)):
@@ -411,7 +426,7 @@ def autoselect_fields_check_can_add(form, model, user):
form_field.check_can_add(user, db_field.rel.to)
-def plugin_options(lookup, channel_name, widget_plugin_options, initial):
+def make_plugin_options(lookup, channel_name, widget_plugin_options, initial):
""" Make a JSON dumped dict of all options for the jQuery ui plugin."""
po = {}
if initial:
@@ -429,3 +444,11 @@ def plugin_options(lookup, channel_name, widget_plugin_options, initial):
'plugin_options': mark_safe(json.dumps(po)),
'data_plugin_options': force_escape(json.dumps(po))
}
+
+
+def pack_ids(ids):
+ if ids:
+ # |pk|pk| of current
+ return "|" + "|".join(str(pk) for pk in ids) + "|"
+ else:
+ return "|"
diff --git a/ajax_select/lookup_channel.py b/ajax_select/lookup_channel.py
index 11adc72..ebed708 100644
--- a/ajax_select/lookup_channel.py
+++ b/ajax_select/lookup_channel.py
@@ -8,7 +8,8 @@ class LookupChannel(object):
"""
Subclass this, setting the model and implementing methods to taste.
- Attributes:
+ Attributes::
+
model (Model): The Django Model that this lookup channel will search for.
plugin_options (dict): Options passed to jQuery UI plugin that are specific to this channel.
min_length (int): Minimum number of characters user types before a search is initiated.
@@ -29,12 +30,13 @@ class LookupChannel(object):
"""
Return a QuerySet searching for the query string `q`.
- Note that you may return any iterable so you can return a list or even use yield and turn this
- method into a generator.
+ Note that you may return any iterable so you can return a list or even
+ use yield and turn this method into a generator.
Args:
q (str, unicode): The query string to search for.
- request (Request): This can be used to customize the search by User or to use additional GET variables.
+ request (Request): This can be used to customize the search by User
+ or to use additional GET variables.
Returns:
(QuerySet, list, generator): iterable of related_models
@@ -43,12 +45,16 @@ class LookupChannel(object):
return self.model.objects.filter(**kwargs).order_by(self.search_field)
def get_result(self, obj):
- """The text result of autocompleting the entered query.
+ """
+ The text result of autocompleting the entered query.
- For a partial string that the user typed in, each matched result is here converted to the fully completed text.
+ For a partial string that the user typed in, each matched result is
+ here converted to the fully completed text.
- This is currently displayed only for a moment in the text field after the user has selected the item.
- Then the item is displayed in the item_display deck and the text field is cleared.
+ This is currently displayed only for a moment in the text field after
+ the user has selected the item.
+ Then the item is displayed in the item_display deck and the text field
+ is cleared.
Args:
obj (Model):
@@ -58,7 +64,8 @@ class LookupChannel(object):
return escape(force_text(obj))
def format_match(self, obj):
- """(HTML) Format item for displaying in the dropdown.
+ """
+ (HTML) Format item for displaying in the dropdown.
Args:
obj (Model):
@@ -68,7 +75,8 @@ class LookupChannel(object):
return escape(force_text(obj))
def format_item_display(self, obj):
- """ (HTML) format item for displaying item in the selected deck area.
+ """
+ (HTML) format item for displaying item in the selected deck area.
Args:
obj (Model):
@@ -78,29 +86,28 @@ class LookupChannel(object):
return escape(force_text(obj))
def get_objects(self, ids):
- """This is used to retrieve the currently selected objects for either ManyToMany or ForeignKey.
-
- Note that the order of the ids supplied for ManyToMany fields is dependent on how the
- objects manager fetches it.
- ie. what is returned by `YourModel.{fieldname}_set.all()`
-
- In most situations (especially postgres) this order is indeterminate -- not the order that you originally
- added them in the interface.
- See :doc:`/Ordered-ManyToMany` for a solution to this.
+ """
+ This is used to retrieve the currently selected objects for either ManyToMany or ForeignKey.
Args:
ids (list): list of primary keys
Returns:
list: list of Model objects
"""
- # return objects in the same order as passed in here
- pk_type = self.model._meta.pk.to_python
+ if self.model._meta.pk.rel is not None:
+ # Use the type of the field being referenced
+ pk_type = self.model._meta.pk.target_field.to_python
+ else:
+ pk_type = self.model._meta.pk.to_python
+
+ # Return objects in the same order as passed in here
ids = [pk_type(pk) for pk in ids]
things = self.model.objects.in_bulk(ids)
return [things[aid] for aid in ids if aid in things]
def can_add(self, user, other_model):
- """Check if the user has permission to add a ForeignKey or M2M model.
+ """
+ Check if the user has permission to add a ForeignKey or M2M model.
This enables the green popup + on the widget.
Default implentation is the standard django permission check.
@@ -116,14 +123,15 @@ class LookupChannel(object):
return user.has_perm("%s.add_%s" % (ctype.app_label, ctype.model))
def check_auth(self, request):
- """By default only request.user.is_staff have access.
+ """
+ By default only request.user.is_staff have access.
This ensures that nobody can get your data by simply knowing the lookup URL.
This is called from the ajax_lookup view.
- Public facing forms (outside of the Admin) should implement this to allow
- non-staff to use this LookupChannel.
+ Public facing forms (outside of the Admin) should implement this to
+ allow non-staff to use this LookupChannel.
Args:
request (Request)
diff --git a/ajax_select/registry.py b/ajax_select/registry.py
index d036f8c..dffd38f 100644
--- a/ajax_select/registry.py
+++ b/ajax_select/registry.py
@@ -13,6 +13,10 @@ class LookupChannelRegistry(object):
_registry = {}
def load_channels(self):
+ """
+ Called when loading the application. Cannot be called a second time,
+ (eg. for testing) as Django will not re-import and re-register anything.
+ """
self._registry = {}
try:
from django.utils.module_loading import autodiscover_modules
diff --git a/ajax_select/static/ajax_select/js/ajax_select.js b/ajax_select/static/ajax_select/js/ajax_select.js
index 644456f..c88416d 100644
--- a/ajax_select/static/ajax_select/js/ajax_select.js
+++ b/ajax_select/static/ajax_select/js/ajax_select.js
@@ -1,6 +1,6 @@
-'use strict';
+(function() {
-(function($) {
+ var $ = window.jQuery;
$.fn.autocompleteselect = function(options) {
return this.each(function() {
@@ -134,26 +134,20 @@
function addAutoComplete (inp, callback) {
var $inp = $(inp),
- html_id = inp.id,
- prefix_id = html_id,
- opts = JSON.parse($inp.attr('data-plugin-options')),
- prefix = 0;
-
- /* detects inline forms and converts the html_id if needed */
- if (html_id.indexOf('__prefix__') !== -1) {
- // Some dirty loop to find the appropriate element to apply the callback to
- while ($('#' + html_id).length) {
- html_id = prefix_id.replace(/__prefix__/, prefix++);
- }
- html_id = prefix_id.replace(/__prefix__/, prefix - 2);
- // Ignore the first call to this function, the one that is triggered when
- // page is loaded just because the 'empty' form is there.
- if ($('#' + html_id + ', #' + html_id + '_text').hasClass('ui-autocomplete-input')) {
- return;
- }
+ opts = JSON.parse($inp.attr('data-plugin-options'));
+ // Do not activate empty-form inline rows.
+ // These are cloned into the form when adding another row and will be activated at that time.
+ if ($inp.attr('id').indexOf('__prefix__') !== -1) {
+ // console.log('skipping __prefix__ row', $inp);
+ return;
}
-
+ if ($inp.data('_ajax_select_inited_')) {
+ // console.log('skipping already activated row', $inp);
+ return;
+ }
+ // console.log('activating', $inp);
callback($inp, opts);
+ $inp.data('_ajax_select_inited_', true);
}
// allow html in the results menu
@@ -187,14 +181,30 @@
}
});
- /* the popup handler
- requires RelatedObjects.js which is part of the django admin js
- so if using outside of the admin then you would need to include that manually */
- window.didAddPopup = function (win, newId, newRepr) {
+ /* Called by the popup create object when it closes.
+ * For the popup this is opener.dismissAddRelatedObjectPopup
+ * Django implements this in RelatedObjectLookups.js
+ * In django >= 1.10 we can rely on input.trigger('change')
+ * and avoid this hijacking.
+ */
+ var djangoDismissAddRelatedObjectPopup = window.dismissAddRelatedObjectPopup || window.dismissAddAnotherPopup;
+ window.dismissAddRelatedObjectPopup = function(win, newId, newRepr) {
+ // Iff this is an ajax-select input then close the window and
+ // trigger didAddPopup
var name = window.windowname_to_id(win.name);
- $('#' + name).trigger('didAddPopup', [window.html_unescape(newId), window.html_unescape(newRepr)]);
- win.close();
- };
+ var input = $('#' + name);
+ if (input.data('ajax-select')) {
+ win.close();
+ // newRepr is django's repr of object
+ // not the Lookup's formatting of it.
+ input.trigger('didAddPopup', [newId, newRepr]);
+ } else {
+ // Call the normal django set and close function.
+ djangoDismissAddRelatedObjectPopup(win, newId, newRepr);
+ }
+ }
+ // Django renamed this function in 1.8
+ window.dismissAddAnotherPopup = window.dismissAddRelatedObjectPopup;
// activate any on page
$(window).bind('init-autocomplete', function() {
@@ -228,10 +238,11 @@
// if dynamically injecting forms onto a page
// you can trigger them to be ajax-selects-ified:
$(window).trigger('init-autocomplete');
+ // When adding new rows in inline forms, reinitialize and activate newly added rows.
$(document)
.on('click', '.inline-group ul.tools a.add, .inline-group div.add-row a, .inline-group .tabular tr.add-row td a', function() {
$(window).trigger('init-autocomplete');
});
});
-})(window.jQuery);
+})();
diff --git a/ajax_select/static/ajax_select/js/bootstrap.js b/ajax_select/static/ajax_select/js/bootstrap.js
index f2f02d8..cb742ea 100644
--- a/ajax_select/static/ajax_select/js/bootstrap.js
+++ b/ajax_select/static/ajax_select/js/bootstrap.js
@@ -1,7 +1,30 @@
-// load jquery and jquery-ui if needed
-// into window.jQuery
-if (typeof window.jQuery === 'undefined') {
- document.write('<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"><\/script><script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.js"><\/script><link type="text/css" rel="stylesheet" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />');
-} else if(typeof window.jQuery.ui === 'undefined' || typeof window.jQuery.ui.autocomplete === 'undefined') {
- document.write('<script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.js"><\/script><link type="text/css" rel="stylesheet" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />');
-}
+(function(w) {
+ /**
+ * load jquery and jquery-ui if needed
+ */
+
+ function not(thing) {
+ return typeof thing === 'undefined';
+ }
+
+ function loadJS(src) {
+ document.write('<script type="text/javascript" src="' + src + '"><\/script>');
+ }
+
+ function loadCSS(href) {
+ var script = document.createElement('link');
+ script.href = href;
+ script.type = 'text/css';
+ script.rel = 'stylesheet';
+ document.head.appendChild(script);
+ }
+
+ if (not(w.jQuery)) {
+ loadJS('//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js');
+ }
+
+ if (not(w.jQuery) || not(w.jQuery.ui) || not(w.jQuery.ui.autocomplete)) {
+ loadJS('//code.jquery.com/ui/1.10.3/jquery-ui.js');
+ loadCSS('//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css');
+ }
+})(window);
diff --git a/ajax_select/urls.py b/ajax_select/urls.py
index 048ca46..e810267 100644
--- a/ajax_select/urls.py
+++ b/ajax_select/urls.py
@@ -4,8 +4,5 @@ from ajax_select import views
urlpatterns = [
url(r'^ajax_lookup/(?P<channel>[-\w]+)$',
views.ajax_lookup,
- name='ajax_lookup'),
- url(r'^add_popup/(?P<app_label>\w+)/(?P<model>\w+)$',
- views.add_popup,
- name='add_popup')
+ name='ajax_lookup')
]
diff --git a/ajax_select/views.py b/ajax_select/views.py
index 65c1667..8716b13 100644
--- a/ajax_select/views.py
+++ b/ajax_select/views.py
@@ -1,11 +1,7 @@
-
-from ajax_select import registry
-from ajax_select.registry import get_model
-from django.contrib.admin import site
-from django.contrib.admin.options import IS_POPUP_VAR
+import json
from django.http import HttpResponse
from django.utils.encoding import force_text
-import json
+from ajax_select import registry
def ajax_lookup(request, channel):
@@ -50,56 +46,6 @@ def ajax_lookup(request, channel):
} for item in instances
])
- return HttpResponse(results, content_type='application/json')
-
-
-def add_popup(request, app_label, model):
- """Presents the admin site popup add view (when you click the green +).
-
- It serves the admin.add_view under a different URL and does some magic fiddling
- to close the popup window after saving and call back to the opening window.
-
- make sure that you have added ajax_select.urls to your urls.py::
- (r'^ajax_select/', include('ajax_select.urls')),
-
- this URL is expected in the code below, so it won't work under a different path
- TODO - check if this is still true.
-
- This view then hijacks the result that the django admin returns
- and instead of calling django's dismissAddAnontherPopup(win,newId,newRepr)
- it calls didAddPopup(win,newId,newRepr) which was added inline with bootstrap.html
- """
-
- themodel = get_model(app_label, model)
- admin = site._registry[themodel]
-
- # TODO : should detect where we really are
- # admin.admin_site.root_path = "/ajax_select/"
-
- # Force the add_view to always recognise that it is being
- # rendered in a pop up context
- if request.method == 'GET':
- get = request.GET.copy()
- get[IS_POPUP_VAR] = 1
- request.GET = get
- elif request.method == 'POST':
- post = request.POST.copy()
- post[IS_POPUP_VAR] = 1
- request.POST = post
-
- response = admin.add_view(request, request.path)
-
- if request.method == 'POST' and (response.status_code == 200):
-
- def fiddle(response):
- content = response.content.decode('UTF-8')
- # django >= 1.8
- fiddled = content.replace('dismissAddRelatedObjectPopup', 'didAddPopup')
- # django < 1.8
- fiddled = fiddled.replace('dismissAddAnotherPopup', 'didAddPopup')
- response.content = fiddled.encode('UTF-8')
- return response
-
- response.add_post_render_callback(fiddle)
-
+ response = HttpResponse(results, content_type='application/json')
+ response['Cache-Control'] = 'max-age=0, must-revalidate, no-store, no-cache;'
return response
diff --git a/django_ajax_selects.egg-info/PKG-INFO b/django_ajax_selects.egg-info/PKG-INFO
index ee5b2b1..0a15fe9 100644
--- a/django_ajax_selects.egg-info/PKG-INFO
+++ b/django_ajax_selects.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-ajax-selects
-Version: 1.4.3
+Version: 1.6.0
Summary: Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.
Home-page: https://github.com/crucialfelix/django-ajax-selects/
Author: Chris Sattinger
@@ -17,8 +17,8 @@ Description: Edit ForeignKey, ManyToManyField and CharField in Django Admin usin
- Integrate with other UI elements elsewhere on the page using the javascript API
- Works in Admin as well as in normal views
- - Django >=1.5, <=1.9
- - Python >=2.7, <=3.5
+ - Django >=1.7, <=2
+ - Python >=2.7, <=3.7
Platform: UNKNOWN
Classifier: Programming Language :: Python
diff --git a/django_ajax_selects.egg-info/SOURCES.txt b/django_ajax_selects.egg-info/SOURCES.txt
index bfd9710..cee4f0b 100644
--- a/django_ajax_selects.egg-info/SOURCES.txt
+++ b/django_ajax_selects.egg-info/SOURCES.txt
@@ -36,6 +36,8 @@ tests/models.py
tests/other_lookups.py
tests/settings.py
tests/test_fields.py
+tests/test_integration.py
+tests/test_lookups.py
tests/test_registry.py
tests/test_views.py
tests/urls.py
\ No newline at end of file
diff --git a/requirements-test.txt b/requirements-test.txt
index 70d2a9d..19ea40d 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,6 +1,6 @@
-coverage
-coveralls
-flake8>=2.1.0
-tox>=1.7.0
-sphinx>=1.3.5
+coverage>=4.4.1
+coveralls>=1.1
+flake8>=3.3.0
+tox>=2.7.0
+sphinx>=1.6.1
sphinx_rtd_theme
diff --git a/requirements.txt b/requirements.txt
index 220c4c4..f90c442 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-django>=1.5.1, <=1.9
-wheel==0.24.0
+django>=1.7, <2
+wheel==0.29.0
diff --git a/setup.py b/setup.py
index ee46357..f9b1902 100644
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,7 @@ except ImportError:
setup(
name='django-ajax-selects',
- version='1.4.3',
+ version='1.6.0',
description='Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.',
author='Chris Sattinger',
author_email='crucialfelix at gmail.com',
@@ -54,7 +54,7 @@ Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI A
- Integrate with other UI elements elsewhere on the page using the javascript API
- Works in Admin as well as in normal views
-- Django >=1.5, <=1.9
-- Python >=2.7, <=3.5
+- Django >=1.7, <=2
+- Python >=2.7, <=3.7
"""
)
diff --git a/tests/admin.py b/tests/admin.py
index 5b5b1ae..bd22e5a 100644
--- a/tests/admin.py
+++ b/tests/admin.py
@@ -1,9 +1,30 @@
from django.contrib import admin
-from tests.models import Author
+from ajax_select.admin import AjaxSelectAdmin, AjaxSelectAdminTabularInline
+from tests.models import Author, Book, Person
+from tests.test_integration import BookForm
-class AuthorAdmin(admin.ModelAdmin):
- pass
+ at admin.register(Book)
+class BookAdmin(AjaxSelectAdmin):
+ form = BookForm
+
+
+class BookInline(AjaxSelectAdminTabularInline):
+
+ model = Book
+ form = BookForm
+ extra = 2
+
-admin.site.register(Author, AuthorAdmin)
+ at admin.register(Author)
+class AuthorAdmin(AjaxSelectAdmin):
+
+ inlines = [
+ BookInline
+ ]
+
+
+ at admin.register(Person)
+class PersonAdmin(admin.ModelAdmin):
+ pass
diff --git a/tests/lookups.py b/tests/lookups.py
index 2b4b741..c014ea4 100644
--- a/tests/lookups.py
+++ b/tests/lookups.py
@@ -1,6 +1,11 @@
+"""
+Testing the register and autoloading.
+
+Should not be used by other tests.
+"""
from django.utils.html import escape
from django.contrib.auth.models import User
-from tests.models import Person
+from tests.models import Person, Author
import ajax_select
@@ -38,3 +43,16 @@ class UserLookup(ajax_select.LookupChannel):
def get_query(self, q, request):
return self.model.objects.filter(email=q)
+
+
+ at ajax_select.register('name')
+class NameLookup(ajax_select.LookupChannel):
+
... 362 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-ajax-selects.git
More information about the Python-modules-commits
mailing list