From 171a0ac5e361f3c7303ebd5af165258ab7424824 Mon Sep 17 00:00:00 2001 From: Xabi Bello Date: Wed, 12 Apr 2017 10:36:22 +0200 Subject: [PATCH 01/14] Changed the build_attrs to work with Django==1.11. --- ajax_select/fields.py | 6 +++--- tox.ini | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 4047ba80a4..0dc90264e1 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -61,7 +61,7 @@ def __init__(self, def render(self, name, value, attrs=None): value = value or '' - final_attrs = self.build_attrs(attrs) + final_attrs = self.build_attrs(self.attrs) self.html_id = final_attrs.pop('id', name) current_repr = '' @@ -179,7 +179,7 @@ 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) self.html_id = final_attrs.pop('id', name) lookup = registry.get(self.channel) @@ -326,7 +326,7 @@ def render(self, name, value, attrs=None): initial = value or '' - final_attrs = self.build_attrs(attrs) + final_attrs = self.build_attrs(self.attrs) self.html_id = final_attrs.pop('id', name) lookup = registry.get(self.channel) diff --git a/tox.ini b/tox.ini index 9f07055699..6d952f3aa0 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ [tox] envlist = {py27,py34}-flake8, - {py27,py34}-dj{16,17,18,19,110} + {py27,py34}-dj{16,17,18,19,110,111} skip_missing_interpreters = true @@ -22,6 +22,7 @@ deps = dj18: Django>=1.8,<1.9 dj19: Django>=1.9,<1.10 dj110: Django>=1.10,<1.11 + dj111: Django>=1.11,<1.12 ; djmaster: https://github.com/django/django/zipball/master [testenv:py27-flake8] From e782ce863a989ddab5089ccccf2ab757c912c4fc Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Tue, 16 May 2017 17:09:15 +0200 Subject: [PATCH 02/14] =?UTF-8?q?fix=20PR=20193=20-=20ignore=20=E2=80=98re?= =?UTF-8?q?quired=E2=80=99=20attr=20when=20rendering=20the=20text=20fields?= =?UTF-8?q?.=20Adds=20a=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ajax_select/fields.py | 3 +++ example/example/forms.py | 2 +- tests/test_fields.py | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 4047ba80a4..29782c55e4 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -62,6 +62,7 @@ def __init__(self, def render(self, name, value, attrs=None): value = value or '' final_attrs = self.build_attrs(attrs) + final_attrs.pop('required', None) self.html_id = final_attrs.pop('id', name) current_repr = '' @@ -180,6 +181,7 @@ def render(self, name, value, attrs=None): value = [] final_attrs = self.build_attrs(attrs) + final_attrs.pop('required', None) self.html_id = final_attrs.pop('id', name) lookup = registry.get(self.channel) @@ -328,6 +330,7 @@ def render(self, name, value, attrs=None): final_attrs = self.build_attrs(attrs) self.html_id = final_attrs.pop('id', name) + final_attrs.pop('required', None) lookup = registry.get(self.channel) if self.show_help_text: diff --git a/example/example/forms.py b/example/example/forms.py index 89b11f54f8..e641453ae1 100644 --- a/example/example/forms.py +++ b/example/example/forms.py @@ -13,7 +13,7 @@ class Meta: exclude = [] # args: this model, fieldname on this model, lookup_channel_name - group = make_ajax_field(Release, 'group', 'group', show_help_text=True) + group = make_ajax_field(Release, 'group', 'group', show_help_text=True, required=True) label = make_ajax_field(Release, 'label', 'label', help_text="Search for label by name") diff --git a/tests/test_fields.py b/tests/test_fields.py index d89161c2a1..412401abb3 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -18,6 +18,15 @@ def test_render_with_value(self): out = widget.render('book', book.pk) self.assertTrue('autocompleteselect' in out) + def test_render_required_field(self): + field = fields.AutoCompleteSelectField('book', required=True) + widget = field.widget + + book = Book.objects.create(name='book') + out = widget.render('book', book.pk) + self.assertTrue('autocompleteselect' in out) + self.assertTrue('required' not in out) + class TestAutoCompleteSelectMultipleWidget(TestCase): From e1d0d7bb9d6571e5bac53adc700333ad950408d8 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Tue, 16 May 2017 17:12:52 +0200 Subject: [PATCH 03/14] Update test requirements; drop support for Django 1.6 --- example/example/admin.py | 19 ++++++------------- requirements-test.txt | 10 +++++----- requirements.txt | 4 ++-- setup.py | 2 +- tests/admin.py | 7 +++---- tox.ini | 2 +- 6 files changed, 18 insertions(+), 26 deletions(-) diff --git a/example/example/admin.py b/example/example/admin.py index c130e58bfa..39b4a76d36 100644 --- a/example/example/admin.py +++ b/example/example/admin.py @@ -6,13 +6,12 @@ from example.models import Person, Label, Group, Song, Release, Book, Author +@admin.register(Person) class PersonAdmin(AjaxSelectAdmin): - pass -admin.site.register(Person, PersonAdmin) - +@admin.register(Label) class LabelAdmin(AjaxSelectAdmin): """ to get + popup buttons, subclass AjaxSelectAdmin @@ -28,8 +27,6 @@ class PersonAdmin(YourAdminSuperclass, AjaxSelectAdmin): # model, fieldlist, [form superclass] form = make_ajax_form(Label, {'owner': 'person'}) -admin.site.register(Label, LabelAdmin) - class ReleaseInline(AjaxSelectAdminStackedInline): @@ -44,6 +41,7 @@ class ReleaseInline(AjaxSelectAdminStackedInline): extra = 1 +@admin.register(Group) class GroupAdmin(AjaxSelectAdmin): # this shows a ManyToMany field @@ -52,9 +50,8 @@ class GroupAdmin(AjaxSelectAdmin): ReleaseInline ] -admin.site.register(Group, GroupAdmin) - +@admin.register(Song) class SongAdmin(AjaxSelectAdmin): form = make_ajax_form(Song, {'group': 'group', 'title': 'cliche'}) @@ -64,17 +61,14 @@ class SongAdmin(AjaxSelectAdmin): # and throws a validation error on save # but doesn't show any error message to the user -admin.site.register(Song, SongAdmin) - +@admin.register(Release) class ReleaseAdmin(AjaxSelectAdmin): # specify a form class manually (normal django way) # see forms.py form = ReleaseForm -admin.site.register(Release, ReleaseAdmin) - class BookInline(AjaxSelectAdminTabularInline): @@ -98,10 +92,9 @@ class BookInline(AjaxSelectAdminTabularInline): # return fs +@admin.register(Author) class AuthorAdmin(AjaxSelectAdmin): inlines = [ BookInline, ] - -admin.site.register(Author, AuthorAdmin) diff --git a/requirements-test.txt b/requirements-test.txt index 70d2a9df3f..19ea40d88b 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 3cb29db69e..cf4a61b78b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -django>=1.6.1, <=1.11 -wheel==0.24.0 +django>=1.7, <=1.11 +wheel==0.29.0 diff --git a/setup.py b/setup.py index f47290140b..eceebfe3db 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ - Integrate with other UI elements elsewhere on the page using the javascript API - Works in Admin as well as in normal views -- Django >=1.6, <=1.10 +- Django >=1.7, <=1.10 - Python >=2.7, <=3.5 """ ) diff --git a/tests/admin.py b/tests/admin.py index b36a2f0dad..bd22e5a63e 100644 --- a/tests/admin.py +++ b/tests/admin.py @@ -5,9 +5,9 @@ from tests.test_integration import BookForm +@admin.register(Book) class BookAdmin(AjaxSelectAdmin): form = BookForm -admin.site.register(Book, BookAdmin) class BookInline(AjaxSelectAdminTabularInline): @@ -17,15 +17,14 @@ class BookInline(AjaxSelectAdminTabularInline): extra = 2 +@admin.register(Author) class AuthorAdmin(AjaxSelectAdmin): inlines = [ BookInline ] -admin.site.register(Author, AuthorAdmin) - +@admin.register(Person) class PersonAdmin(admin.ModelAdmin): pass -admin.site.register(Person, PersonAdmin) diff --git a/tox.ini b/tox.ini index 9f07055699..213b1bc811 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ [tox] envlist = {py27,py34}-flake8, - {py27,py34}-dj{16,17,18,19,110} + {py27,py34}-dj{17,18,19,110} skip_missing_interpreters = true From 72abbe6e0693db3853655673424d34dc884a679a Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Tue, 16 May 2017 17:43:12 +0200 Subject: [PATCH 04/14] fix: build_attrs is supposed to merge self.attrs and any supplied attrs --- ajax_select/fields.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 0dc90264e1..1b404dec38 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -61,7 +61,7 @@ def __init__(self, def render(self, name, value, attrs=None): value = value or '' - final_attrs = self.build_attrs(self.attrs) + final_attrs = self.build_attrs(self.attrs, attrs) self.html_id = final_attrs.pop('id', name) current_repr = '' @@ -179,7 +179,7 @@ def render(self, name, value, attrs=None): if value is None: value = [] - final_attrs = self.build_attrs(self.attrs) + final_attrs = self.build_attrs(self.attrs, attrs) self.html_id = final_attrs.pop('id', name) lookup = registry.get(self.channel) @@ -325,8 +325,8 @@ def __init__(self, channel, *args, **kwargs): def render(self, name, value, attrs=None): initial = value or '' + final_attrs = self.build_attrs(self.attrs, attrs) - final_attrs = self.build_attrs(self.attrs) self.html_id = final_attrs.pop('id', name) lookup = registry.get(self.channel) From 56e7a5352a04acc9bfb4cbf75b98a95bc1e0a8d9 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Tue, 16 May 2017 20:03:12 +0200 Subject: [PATCH 05/14] travis: Remove django 1.6, add 1.11 --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2dc0480f7c..77f6543dcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,18 +3,16 @@ sudo: false env: - TOX_ENV=py27-flake8 - TOX_ENV=py34-flake8 - - TOX_ENV=py27-dj16 - - TOX_ENV=py33-dj16 - TOX_ENV=py27-dj17 - TOX_ENV=py27-dj18 - TOX_ENV=py27-dj19 - TOX_ENV=py27-dj110 + - TOX_ENV=py27-dj111 - TOX_ENV=py34-dj17 - TOX_ENV=py34-dj18 - TOX_ENV=py34-dj19 - TOX_ENV=py34-dj110 - # - TOX_ENV=py35-dj18 - # - TOX_ENV=py35-dj19 + - TOX_ENV=py34-dj111 install: - pip install -r requirements-test.txt script: From 049fc53b7dc02be7bdd65e0955f0ec0cdb687451 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 09:12:19 +0200 Subject: [PATCH 06/14] Add python 3.6 to testing --- .travis.yml | 6 ++++++ tox.ini | 17 ++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 77f6543dcd..56b24102df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ sudo: false env: - TOX_ENV=py27-flake8 - TOX_ENV=py34-flake8 + - TOX_ENV=py36-flake8 - TOX_ENV=py27-dj17 - TOX_ENV=py27-dj18 - TOX_ENV=py27-dj19 @@ -13,6 +14,11 @@ env: - TOX_ENV=py34-dj19 - TOX_ENV=py34-dj110 - TOX_ENV=py34-dj111 + - TOX_ENV=py36-dj17 + - TOX_ENV=py36-dj18 + - TOX_ENV=py36-dj19 + - TOX_ENV=py36-dj110 + - TOX_ENV=py36-dj111 install: - pip install -r requirements-test.txt script: diff --git a/tox.ini b/tox.ini index 16a9083771..ece7b4aa37 100644 --- a/tox.ini +++ b/tox.ini @@ -6,9 +6,8 @@ ; [tox] envlist = - {py27,py34}-flake8, - {py27,py34}-{dj17,dj18,dj19,dj110,dj111} -skip_missing_interpreters = true + {py27,py34,py36}-flake8, + {py27,py34,py36}-{dj17,dj18,dj19,dj110,dj111} [testenv] @@ -38,12 +37,12 @@ commands = flake8 ajax_select tests example ; deps = ; flake8 ; commands = flake8 ajax_select tests example -; -; [testenv:py36-flake8] -; deps = -; flake8 -; commands = flake8 ajax_select tests example -; + +[testenv:py36-flake8] +deps = + flake8 +commands = flake8 ajax_select tests example + ; [testenv:py37-flake8] ; deps = ; flake8 From 7b63c9f1bc69f18193d7c422d2ef890a9658036a Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 09:20:15 +0200 Subject: [PATCH 07/14] Revert testing for python 3.6 Due to travis issue: https://github.com/travis-ci/travis-ci/issues/4990 --- .travis.yml | 6 ------ tox.ini | 12 ++++++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 56b24102df..77f6543dcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false env: - TOX_ENV=py27-flake8 - TOX_ENV=py34-flake8 - - TOX_ENV=py36-flake8 - TOX_ENV=py27-dj17 - TOX_ENV=py27-dj18 - TOX_ENV=py27-dj19 @@ -14,11 +13,6 @@ env: - TOX_ENV=py34-dj19 - TOX_ENV=py34-dj110 - TOX_ENV=py34-dj111 - - TOX_ENV=py36-dj17 - - TOX_ENV=py36-dj18 - - TOX_ENV=py36-dj19 - - TOX_ENV=py36-dj110 - - TOX_ENV=py36-dj111 install: - pip install -r requirements-test.txt script: diff --git a/tox.ini b/tox.ini index ece7b4aa37..9a4c323849 100644 --- a/tox.ini +++ b/tox.ini @@ -6,8 +6,8 @@ ; [tox] envlist = - {py27,py34,py36}-flake8, - {py27,py34,py36}-{dj17,dj18,dj19,dj110,dj111} + {py27,py34}-flake8, + {py27,py34}-{dj17,dj18,dj19,dj110,dj111} [testenv] @@ -38,10 +38,10 @@ commands = flake8 ajax_select tests example ; flake8 ; commands = flake8 ajax_select tests example -[testenv:py36-flake8] -deps = - flake8 -commands = flake8 ajax_select tests example +; [testenv:py36-flake8] +; deps = +; flake8 +; commands = flake8 ajax_select tests example ; [testenv:py37-flake8] ; deps = From 691dce501ae4a86c1c8568dc3170596dbbf71112 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 09:40:33 +0200 Subject: [PATCH 08/14] =?UTF-8?q?Import=20django=E2=80=99s=20reverse=20fro?= =?UTF-8?q?m=20new=20location=20(1.10+),=20fallback=20to=20old=20location?= =?UTF-8?q?=20Remove=20fallback=20for=20import=20flatatt=20for=20django=20?= =?UTF-8?q?<=201.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix from @coagulant : https://github.com/crucialfelix/django-ajax-selects/pull/198/commits/f8431438a187a26c059e025b005c591120cceea0 --- ajax_select/fields.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 27b8f869b0..753bd442e6 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -4,19 +4,19 @@ from django import forms from django.conf import settings from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse from django.db.models.query import QuerySet -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.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 _ +try: + from django.urls import reverse +except ImportError: + # < django 1.10 + from django.core.urlresolvers import reverse as_default_help = 'Enter text to search.' From 6ebe85f3b388184092a35fce716deff680a0a3e1 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 09:51:14 +0200 Subject: [PATCH 09/14] Update makefiles --- Makefile | 2 +- example/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0c19385584..22821b78ff 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ clean-pyc: find . -name '*~' -exec rm -f {} + lint: - flake8 . + flake8 ajax_select tests example test: tox diff --git a/example/Makefile b/example/Makefile index 0d2eb15541..0ffeae7a3e 100644 --- a/example/Makefile +++ b/example/Makefile @@ -19,7 +19,7 @@ cleandb: help: @echo make install @echo or: - @echo make clean install DJANGO=1.4.2 + @echo make clean install DJANGO=1.11 .PHONY: install clean cleandb help From a1279fa81fac24ab711971bbbd8d4e7065928688 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 09:51:30 +0200 Subject: [PATCH 10/14] formatting: line length --- ajax_select/fields.py | 38 ++++++++++++++++++++++------------- ajax_select/lookup_channel.py | 38 ++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 753bd442e6..2692c45b8f 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -35,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) @@ -134,7 +136,8 @@ def clean(self, value): 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: @@ -152,12 +155,14 @@ def has_changed(self, initial, data): 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) @@ -234,7 +239,9 @@ def id_for_label(self, id_): 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 @@ -250,8 +257,8 @@ def __init__(self, channel, *args, **kwargs): 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): @@ -303,14 +310,15 @@ def has_changed(self, initial_value, data_value): 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) @@ -358,7 +366,8 @@ def render(self, name, value, attrs=None): 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 @@ -381,7 +390,7 @@ def __init__(self, channel, *args, **kwargs): super(AutoCompleteField, self).__init__(*args, **defaults) -#################################################################################### +############################################################################### def _check_can_add(self, user, related_model): """ @@ -408,7 +417,8 @@ def _check_can_add(self, user, related_model): 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)): diff --git a/ajax_select/lookup_channel.py b/ajax_select/lookup_channel.py index 11adc72e65..9c137b6b2b 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 @@ def get_query(self, q, request): """ 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 @@ def get_query(self, q, request): 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 @@ def get_result(self, obj): 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 @@ def format_match(self, obj): 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): @@ -100,7 +108,8 @@ def get_objects(self, 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 +125,15 @@ def can_add(self, user, other_model): 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) From 3c80b9d34c092b450940e6ded6c2e8a3762d5eac Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 10:27:21 +0200 Subject: [PATCH 11/14] Cast type using the type of the field being referenced. Usually this is just the pk type (int or string), but if this is an inherited model then it has a rel using a OneToOneField pointing to the base model. Fixes: https://github.com/crucialfelix/django-ajax-selects/issues/153 Solution from here: https://github.com/crucialfelix/django-ajax-selects/pull/203 (original fork from @adsva is deleted so I cannot test and merge that) --- ajax_select/lookup_channel.py | 20 +++++++++----------- tests/test_lookups.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 tests/test_lookups.py diff --git a/ajax_select/lookup_channel.py b/ajax_select/lookup_channel.py index 9c137b6b2b..ebed70801c 100644 --- a/ajax_select/lookup_channel.py +++ b/ajax_select/lookup_channel.py @@ -86,23 +86,21 @@ def format_item_display(self, obj): 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] diff --git a/tests/test_lookups.py b/tests/test_lookups.py new file mode 100644 index 0000000000..839392dc4c --- /dev/null +++ b/tests/test_lookups.py @@ -0,0 +1,21 @@ + +from django.test import TestCase +from django.contrib.auth.models import User +from .lookups import UserLookup + + +class TestLookups(TestCase): + + def test_get_objects(self): + user1 = User.objects.create(username='user1', + email='user1@example.com', + password='password') + user2 = User.objects.create(username='user2', + email='user2@example.com', + password='password') + lookup = UserLookup() + users = lookup.get_objects([user2.id, user1.id]) + self.assertEqual(len(users), 2) + u2, u1 = users + self.assertEqual(u1, user1) + self.assertEqual(u2, user2) From 2411d39af16b543d9b934d9612d47c449d2d1d37 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 10:56:40 +0200 Subject: [PATCH 12/14] Update supported django and python versions --- requirements.txt | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index cf4a61b78b..f90c442d0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -django>=1.7, <=1.11 +django>=1.7, <2 wheel==0.29.0 diff --git a/setup.py b/setup.py index eceebfe3db..f91a76f6b8 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ - Integrate with other UI elements elsewhere on the page using the javascript API - Works in Admin as well as in normal views -- Django >=1.7, <=1.10 -- Python >=2.7, <=3.5 +- Django >=1.7, <=2 +- Python >=2.7, <=3.7 """ ) From 12dacffa27aa596bd7c75491bc1c59e65432c75f Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 11:07:22 +0200 Subject: [PATCH 13/14] gitignore build directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1e5e8846a3..03f73e9834 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ example/ajax_select example/ajax_selects_example_db dist MANIFEST +build From 69a8cbbc1c13a1999c5f2bcf5ee8b75c8677d0d7 Mon Sep 17 00:00:00 2001 From: Chris Sattinger Date: Wed, 17 May 2017 11:06:25 +0200 Subject: [PATCH 14/14] Bump version, changelog, release notes --- CHANGELOG.md | 14 ++++++++++++++ ajax_select/__init__.py | 2 +- docs/source/Release-notes.rst | 6 ++++++ setup.py | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d6fd7688a..40e0a0b996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # 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) diff --git a/ajax_select/__init__.py b/ajax_select/__init__.py index 77bd56ac62..a4381dc9e5 100644 --- a/ajax_select/__init__.py +++ b/ajax_select/__init__.py @@ -1,5 +1,5 @@ """JQuery-Ajax Autocomplete fields for Django Forms.""" -__version__ = "1.5.2" +__version__ = "1.6.0" __author__ = "crucialfelix" __contact__ = "crucialfelix@gmail.com" __homepage__ = "https://github.com/crucialfelix/django-ajax-selects/" diff --git a/docs/source/Release-notes.rst b/docs/source/Release-notes.rst index e5089a3b4b..bfeb73b00b 100644 --- a/docs/source/Release-notes.rst +++ b/docs/source/Release-notes.rst @@ -3,6 +3,12 @@ Release Notes See also CHANGELOG.md for github issues and bugfixes +1.6.0 +===== + +- Added Support for Django 1.11 +- Dropped Django 1.6 + 1.5.0 ===== diff --git a/setup.py b/setup.py index f91a76f6b8..f9b1902bd1 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='django-ajax-selects', - version='1.5.2', + version='1.6.0', description='Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.', author='Chris Sattinger', author_email='crucialfelix@gmail.com',