[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