diff --git a/.travis.yml b/.travis.yml index 8d19292a92..8eaf2c4af9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,26 @@ language: python +cache: + directories: + - $HOME/.cache/pip + - /opt/python sudo: false env: - TOX_ENV=py27-flake8 - - TOX_ENV=py36-flake8 + - TOX_ENV=py37-flake8 - TOX_ENV=py27-dj18 - TOX_ENV=py27-dj19 - TOX_ENV=py27-dj110 - TOX_ENV=py27-dj111 - - TOX_ENV=py36-dj18 - - TOX_ENV=py36-dj19 - - TOX_ENV=py36-dj110 - - TOX_ENV=py36-dj111 - - TOX_ENV=py36-dj20 - - TOX_ENV=py36-dj21 + - TOX_ENV=py37-dj18 + - TOX_ENV=py37-dj19 + - TOX_ENV=py37-dj110 + - TOX_ENV=py37-dj111 + - TOX_ENV=py37-dj20 + - TOX_ENV=py37-dj21 before_install: - - pyenv global system 3.6 + - pyenv install 2.7 --skip-existing + - pyenv install 3.7 --skip-existing + - pyenv global system 3.7 install: - pip install -r requirements-test.txt script: diff --git a/CHANGELOG.md b/CHANGELOG.md index d1e5bcdef8..ee45cc969f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [1.8.0] + +Added/fixed support for Django 2.2 + ## [1.6.1](https://github.com/crucialfelix/django-ajax-selects/tree/1.6.1) (2017-09-09) [Full Changelog](https://github.com/crucialfelix/django-ajax-selects/compare/1.6.0...1.6.1) diff --git a/README.md b/README.md index d6279866e3..ada5a42b84 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,8 @@ Read the full documention here: [outside of the admin](http://django-ajax-select ## Compatibility -* Django >=1.8, <=2.1 -* Python >=2.7, 3.3+ +* Django >=1.8, <3.0 +* Python >=2.7, >=3.5 ## Contributors diff --git a/ajax_select/admin.py b/ajax_select/admin.py index 31f0468577..4960cf4999 100644 --- a/ajax_select/admin.py +++ b/ajax_select/admin.py @@ -1,9 +1,9 @@ from django.contrib import admin + from ajax_select.fields import autoselect_fields_check_can_add class AjaxSelectAdmin(admin.ModelAdmin): - """ in order to get + popup functions subclass this or do the same hook inside of your get_form """ def get_form(self, request, obj=None, **kwargs): diff --git a/ajax_select/apps.py b/ajax_select/apps.py index f0f28b2bbc..a76725b056 100644 --- a/ajax_select/apps.py +++ b/ajax_select/apps.py @@ -2,7 +2,6 @@ class AjaxSelectConfig(AppConfig): - """ Django 1.7+ enables initializing installed applications and autodiscovering modules. diff --git a/ajax_select/fields.py b/ajax_select/fields.py index 57e82a28b6..78157060a3 100644 --- a/ajax_select/fields.py +++ b/ajax_select/fields.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals import json - from django import forms from django.conf import settings from django.contrib.contenttypes.models import ContentType @@ -10,10 +9,10 @@ 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.module_loading import import_string from django.utils.safestring import mark_safe from django.utils.six import text_type from django.utils.translation import ugettext as _ -from django.utils.module_loading import import_string from ajax_select.registry import registry @@ -23,20 +22,19 @@ # < django 1.10 from django.core.urlresolvers import reverse - as_default_help = 'Enter text to search.' def _media(self): - # unless AJAX_SELECT_BOOTSTRAP == False - # then load jquery and jquery ui + default css - # where needed - js = ('ajax_select/js/bootstrap.js', 'ajax_select/js/ajax_select.js') - try: - if not settings.AJAX_SELECT_BOOTSTRAP: - js = ('ajax_select/js/ajax_select.js',) - except AttributeError: - pass + js = ['admin/js/jquery.init.js'] + + # Unless AJAX_SELECT_BOOTSTRAP == False + # then load include bootstrap which will load jquery and jquery ui + default css as needed + if getattr(settings, "AJAX_SELECT_BOOTSTRAP", True): + js.append('ajax_select/js/bootstrap.js') + + js.append('ajax_select/js/ajax_select.js') + return forms.Media(css={'all': ('ajax_select/css/ajax_select.css',)}, js=js) @@ -48,7 +46,6 @@ def _media(self): class AutoCompleteSelectWidget(forms.widgets.TextInput): - """ Widget to search for a model and return it as text for use in a CharField. """ @@ -106,7 +103,7 @@ def render(self, name, value, attrs=None, renderer=None, **_kwargs): 'add_link': self.add_link, } context.update( - make_plugin_options(lookup, self.channel, self.plugin_options, initial)) + make_plugin_options(lookup, self.channel, self.plugin_options, initial)) templates = ( 'ajax_select/autocompleteselect_%s.html' % self.channel, 'ajax_select/autocompleteselect.html') @@ -121,7 +118,6 @@ def id_for_label(self, id_): class AutoCompleteSelectField(forms.fields.CharField): - """Form field to select a Model for a ForeignKey db field.""" channel = None @@ -130,15 +126,15 @@ def __init__(self, channel, *args, **kwargs): self.channel = channel widget_kwargs = dict( - channel=channel, - help_text=kwargs.get('help_text', _(as_default_help)), - show_help_text=kwargs.pop('show_help_text', True), - plugin_options=kwargs.pop('plugin_options', {}) + channel=channel, + help_text=kwargs.get('help_text', _(as_default_help)), + show_help_text=kwargs.pop('show_help_text', True), + plugin_options=kwargs.pop('plugin_options', {}) ) widget_kwargs.update(kwargs.pop('widget_options', {})) kwargs["widget"] = AutoCompleteSelectWidget(**widget_kwargs) super(AutoCompleteSelectField, self).__init__( - max_length=255, *args, **kwargs) + max_length=255, *args, **kwargs) def clean(self, value): if value: @@ -150,7 +146,7 @@ def clean(self, value): # 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)) + "%s cannot find object: %s" % (lookup, value)) return objs[0] else: if self.required: @@ -171,7 +167,6 @@ def has_changed(self, initial, data): class AutoCompleteSelectMultipleWidget(forms.widgets.SelectMultiple): - """ Widget to select multiple models for a ManyToMany db field. """ @@ -231,7 +226,7 @@ def render(self, name, value, attrs=None, renderer=None, **_kwargs): 'current': value, 'current_ids': current_ids, 'current_reprs': mark_safe( - json.dumps(initial, cls=json_encoder) + json.dumps(initial, cls=json_encoder) ), 'help_text': help_text, 'extra_attrs': mark_safe(flatatt(final_attrs)), @@ -239,7 +234,7 @@ def render(self, name, value, attrs=None, renderer=None, **_kwargs): 'add_link': self.add_link, } context.update( - make_plugin_options(lookup, self.channel, self.plugin_options, initial)) + 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) @@ -254,7 +249,6 @@ def id_for_label(self, id_): class AutoCompleteSelectMultipleField(forms.fields.CharField): - """ Form field to select multiple models for a ManyToMany db field. """ @@ -286,7 +280,7 @@ def __init__(self, channel, *args, **kwargs): django_default_help = _(dh).translate(settings.LANGUAGE_CODE) if django_default_help in translated: cleaned_help = translated.replace( - django_default_help, '').strip() + django_default_help, '').strip() # probably will not show up in translations if cleaned_help: help_text = cleaned_help @@ -327,11 +321,11 @@ 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 @@ -376,14 +370,13 @@ def render(self, name, value, attrs=None, renderer=None, **_kwargs): 'func_slug': self.html_id.replace("-", ""), } context.update( - make_plugin_options(lookup, self.channel, self.plugin_options, initial)) + 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)) class AutoCompleteField(forms.CharField): - """ A CharField that uses an AutoCompleteWidget to lookup matching and stores the result as plain text. @@ -394,9 +387,9 @@ def __init__(self, channel, *args, **kwargs): self.channel = channel widget_kwargs = dict( - help_text=kwargs.get('help_text', _(as_default_help)), - show_help_text=kwargs.pop('show_help_text', True), - plugin_options=kwargs.pop('plugin_options', {}) + help_text=kwargs.get('help_text', _(as_default_help)), + show_help_text=kwargs.pop('show_help_text', True), + plugin_options=kwargs.pop('plugin_options', {}) ) widget_kwargs.update(kwargs.pop('widget_options', {})) if 'attrs' in kwargs: @@ -431,7 +424,7 @@ def _check_can_add(self, user, related_model): 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' + 'admin:%s_%s_add' % (app_label, model)) + '?_popup=1' def autoselect_fields_check_can_add(form, model, user): @@ -466,7 +459,7 @@ def make_plugin_options(lookup, channel_name, widget_plugin_options, initial): return { 'plugin_options': mark_safe(json.dumps(po, cls=json_encoder)), 'data_plugin_options': force_escape( - json.dumps(po, cls=json_encoder) + json.dumps(po, cls=json_encoder) ) } diff --git a/ajax_select/helpers.py b/ajax_select/helpers.py index d456b64aad..727d9def01 100644 --- a/ajax_select/helpers.py +++ b/ajax_select/helpers.py @@ -99,14 +99,14 @@ def make_ajax_field(related_model, fieldname_on_model, channel, show_help_text=F kwargs['show_help_text'] = show_help_text if isinstance(field, ManyToManyField): f = AutoCompleteSelectMultipleField( - channel, - **kwargs) + channel, + **kwargs) elif isinstance(field, ForeignKey): f = AutoCompleteSelectField( - channel, - **kwargs) + channel, + **kwargs) else: f = AutoCompleteField( - channel, - **kwargs) + channel, + **kwargs) return f diff --git a/ajax_select/lookup_channel.py b/ajax_select/lookup_channel.py index a32063c001..c855e0da02 100644 --- a/ajax_select/lookup_channel.py +++ b/ajax_select/lookup_channel.py @@ -4,7 +4,6 @@ class LookupChannel(object): - """ Subclass this, setting the model and implementing methods to taste. diff --git a/ajax_select/registry.py b/ajax_select/registry.py index b69486b71e..40f23048a7 100644 --- a/ajax_select/registry.py +++ b/ajax_select/registry.py @@ -5,7 +5,6 @@ class LookupChannelRegistry(object): - """ Registry for LookupChannels activated for your django project. @@ -57,7 +56,7 @@ def get(self, channel): lookup_spec = self._registry[channel] except KeyError: raise ImproperlyConfigured( - "No ajax_select LookupChannel named %(channel)r is registered." % {'channel': channel}) + "No ajax_select LookupChannel named %(channel)r is registered." % {'channel': channel}) if (type(lookup_spec) is type) and issubclass(lookup_spec, LookupChannel): return lookup_spec() @@ -96,7 +95,6 @@ def make_channel(self, app_model, arg_search_field): app_label, model_name = app_model.split(".") class MadeLookupChannel(LookupChannel): - model = get_model(app_label, model_name) search_field = arg_search_field diff --git a/ajax_select/urls.py b/ajax_select/urls.py index e810267889..cb30b2d196 100644 --- a/ajax_select/urls.py +++ b/ajax_select/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import url + from ajax_select import views urlpatterns = [ diff --git a/ajax_select/views.py b/ajax_select/views.py index 8716b13661..90ae258cdc 100644 --- a/ajax_select/views.py +++ b/ajax_select/views.py @@ -1,11 +1,11 @@ import json from django.http import HttpResponse from django.utils.encoding import force_text + from ajax_select import registry def ajax_lookup(request, channel): - """Load the named lookup channel and lookup matching models. GET or POST should contain 'term' diff --git a/docs/source/conf.py b/docs/source/conf.py index e09619366e..e2f4b29c9c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -172,40 +172,39 @@ # html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'django-ajax-selectsdoc' - # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', + # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', + # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - #'preamble': '', + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples @@ -218,23 +217,23 @@ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -247,7 +246,7 @@ ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -262,13 +261,13 @@ ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/example/example/admin.py b/example/example/admin.py index 39b4a76d36..98e0a4167a 100644 --- a/example/example/admin.py +++ b/example/example/admin.py @@ -1,9 +1,9 @@ - from django.contrib import admin -from ajax_select import make_ajax_form -from ajax_select.admin import AjaxSelectAdmin, AjaxSelectAdminTabularInline, AjaxSelectAdminStackedInline from example.forms import ReleaseForm -from example.models import Person, Label, Group, Song, Release, Book, Author +from example.models import Author, Book, Group, Label, Person, Release, Song + +from ajax_select import make_ajax_form +from ajax_select.admin import AjaxSelectAdmin, AjaxSelectAdminStackedInline, AjaxSelectAdminTabularInline @admin.register(Person) @@ -29,7 +29,6 @@ class PersonAdmin(YourAdminSuperclass, AjaxSelectAdmin): class ReleaseInline(AjaxSelectAdminStackedInline): - # Example of the stacked inline model = Release @@ -43,7 +42,6 @@ class ReleaseInline(AjaxSelectAdminStackedInline): @admin.register(Group) class GroupAdmin(AjaxSelectAdmin): - # this shows a ManyToMany field form = make_ajax_form(Group, {'members': 'person'}) inlines = [ @@ -53,7 +51,6 @@ class GroupAdmin(AjaxSelectAdmin): @admin.register(Song) class SongAdmin(AjaxSelectAdmin): - form = make_ajax_form(Song, {'group': 'group', 'title': 'cliche'}) # django bug: # readonly_fields = ('group',) @@ -64,22 +61,20 @@ class SongAdmin(AjaxSelectAdmin): @admin.register(Release) class ReleaseAdmin(AjaxSelectAdmin): - # specify a form class manually (normal django way) # see forms.py form = ReleaseForm class BookInline(AjaxSelectAdminTabularInline): - # AjaxSelectAdminTabularInline enables the + add option model = Book form = make_ajax_form(Book, { - 'about_group': 'group', - 'mentions_persons': 'person' - }, - show_help_text=True) + 'about_group': 'group', + 'mentions_persons': 'person' + }, + show_help_text=True) extra = 2 # to enable the + add option @@ -94,7 +89,6 @@ class BookInline(AjaxSelectAdminTabularInline): @admin.register(Author) class AuthorAdmin(AjaxSelectAdmin): - inlines = [ BookInline, ] diff --git a/example/example/forms.py b/example/example/forms.py index e641453ae1..327d0fd2ad 100644 --- a/example/example/forms.py +++ b/example/example/forms.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + from django.forms.models import ModelForm -from ajax_select import make_ajax_field from example.models import Release +from ajax_select import make_ajax_field -class ReleaseForm(ModelForm): +class ReleaseForm(ModelForm): class Meta: model = Release exclude = [] diff --git a/example/example/lookups.py b/example/example/lookups.py index 0be95967a9..e6e08301b8 100644 --- a/example/example/lookups.py +++ b/example/example/lookups.py @@ -1,14 +1,15 @@ from __future__ import unicode_literals -from django.utils.six import text_type + from django.db.models import Q from django.utils.html import escape -from example.models import Person, Group, Song -from ajax_select import LookupChannel +from django.utils.six import text_type +from example.models import Group, Person, Song + import ajax_select +from ajax_select import LookupChannel class PersonLookup(LookupChannel): - model = Person def get_query(self, q, request): @@ -29,7 +30,6 @@ def format_item_display(self, obj): class GroupLookup(LookupChannel): - model = Group def get_query(self, q, request): @@ -53,7 +53,6 @@ def can_add(self, user, model): class SongLookup(LookupChannel): - model = Song def get_query(self, q, request): @@ -72,7 +71,6 @@ def format_item_display(self, obj): # Here using decorator syntax rather than settings.AJAX_LOOKUP_CHANNELS @ajax_select.register('cliche') class ClicheLookup(LookupChannel): - """ an autocomplete lookup does not need to search models though the words here could also be stored in a model and searched as in the lookups above diff --git a/example/example/models.py b/example/example/models.py index 4d02751182..bb95b218da 100644 --- a/example/example/models.py +++ b/example/example/models.py @@ -1,13 +1,13 @@ # -*- coding: utf8 -*- from __future__ import unicode_literals + from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Person(models.Model): - """ an actual singular human being """ name = models.CharField(blank=True, max_length=100) email = models.EmailField() @@ -18,13 +18,12 @@ def __str__(self): @python_2_unicode_compatible class Group(models.Model): - """ a music group """ name = models.CharField(max_length=200, unique=True, help_text="Name of the group") members = models.ManyToManyField(Person, - blank=True, - help_text="Enter text to search for and add each member of the group.") + blank=True, + help_text="Enter text to search for and add each member of the group.") url = models.URLField(blank=True) def __str__(self): @@ -33,7 +32,6 @@ def __str__(self): @python_2_unicode_compatible class Label(models.Model): - """ a record label """ name = models.CharField(max_length=200, unique=True) @@ -46,7 +44,6 @@ def __str__(self): @python_2_unicode_compatible class Song(models.Model): - """ a song """ title = models.CharField(blank=False, max_length=200) @@ -58,15 +55,14 @@ def __str__(self): @python_2_unicode_compatible class Release(models.Model): - """ a music release/product """ title = models.CharField(max_length=100) catalog = models.CharField(blank=True, max_length=100) group = models.ForeignKey(Group, blank=True, null=True, - verbose_name="Русский текст (group)", - on_delete=models.CASCADE) + verbose_name="Русский текст (group)", + on_delete=models.CASCADE) label = models.ForeignKey(Label, blank=False, null=False, on_delete=models.CASCADE) songs = models.ManyToManyField(Song, blank=True) @@ -76,7 +72,6 @@ def __str__(self): @python_2_unicode_compatible class Author(models.Model): - """ Author has multiple books, via foreign keys """ @@ -89,7 +84,6 @@ def __str__(self): @python_2_unicode_compatible class Book(models.Model): - """ Book has no admin, its an inline in the Author admin""" author = models.ForeignKey(Author, on_delete=models.CASCADE) diff --git a/example/example/settings.py b/example/example/settings.py index 777dee1524..19322f9856 100644 --- a/example/example/settings.py +++ b/example/example/settings.py @@ -47,7 +47,6 @@ 'song': ('example.lookups', 'SongLookup'), } - # By default will use window.jQuery # or Django Admin's jQuery # or load one from google ajax apis @@ -74,10 +73,10 @@ DATABASE_ENGINE = 'sqlite3' DATABASE_NAME = 'ajax_selects_example_db' -DATABASE_USER = '' # Not used with sqlite3. -DATABASE_PASSWORD = '' # Not used with sqlite3. -DATABASE_HOST = '' # Not used with sqlite3. -DATABASE_PORT = '' # Not used with sqlite3. +DATABASE_USER = '' # Not used with sqlite3. +DATABASE_PASSWORD = '' # Not used with sqlite3. +DATABASE_HOST = '' # Not used with sqlite3. +DATABASE_PORT = '' # Not used with sqlite3. DATABASES = { 'default': { @@ -104,7 +103,6 @@ # to load the internationalization machinery. USE_I18N = True - # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' @@ -122,7 +120,6 @@ # Make this unique, and don't share it with nobody. SECRET_KEY = '=9fhrrwrazha6r_m)r#+in*@n@i322ubzy4r+zz%wz$+y(=qpb' - ROOT_URLCONF = 'example.urls' # Django < 1.8 diff --git a/example/example/urls.py b/example/example/urls.py index df1b1c9ac6..8b444d7394 100644 --- a/example/example/urls.py +++ b/example/example/urls.py @@ -1,17 +1,17 @@ -from django.conf.urls import url, include +from django.conf import settings +from django.conf.urls import include, url from django.conf.urls.static import static from django.contrib import admin -from django.conf import settings + from ajax_select import urls as ajax_select_urls from example import views - admin.autodiscover() urlpatterns = [ - url(r'^search_form', - view=views.search_form, - name='search_form'), - url(r'^admin/lookups/', include(ajax_select_urls)), - url(r'^admin/', admin.site.urls), -] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + url(r'^search_form', + view=views.search_form, + name='search_form'), + url(r'^admin/lookups/', include(ajax_select_urls)), + url(r'^admin/', admin.site.urls), + ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/example/example/views.py b/example/example/views.py index 127eb774cb..b605e546d3 100644 --- a/example/example/views.py +++ b/example/example/views.py @@ -1,23 +1,23 @@ # -*- coding: utf-8 -*- - from __future__ import unicode_literals + from django import forms from django.shortcuts import render_to_response from django.template import RequestContext + from ajax_select.fields import AutoCompleteField class SearchForm(forms.Form): - q = AutoCompleteField('cliche', - required=True, - help_text="Autocomplete will suggest clichés about cats, but you can enter anything you like.", - label="Favorite Cliché", - attrs={'size': 100}) + required=True, + help_text="Autocomplete will suggest clichés about cats, but " + "you can enter anything you like.", + label="Favorite Cliché", + attrs={'size': 100}) def search_form(request): - dd = {} if 'q' in request.GET: dd['entered'] = request.GET.get('q') diff --git a/example/templates/search_form.html b/example/templates/search_form.html index d87b80a87e..052e5542a9 100644 --- a/example/templates/search_form.html +++ b/example/templates/search_form.html @@ -1,19 +1,21 @@ - - {% if entered %} -

You entered:

{{ entered }}

- {% endif %} - -
- - {{ form }} - - - -
-
- + +{% if entered %} +

You entered: +

{{ entered }}

+{% endif %} + +
+ + {{ form }} + + + + +
+
+ \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 01fd0eb060..f5651852ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ django>=1.8, <2.2 -wheel==0.29.0 +wheel>=0.29.0 diff --git a/setup.py b/setup.py index 59d9816829..5d6d22262c 100644 --- a/setup.py +++ b/setup.py @@ -3,23 +3,22 @@ from setuptools import setup setup( - name='django-ajax-selects', - version='1.7.1', - description='Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.', - author='Chris Sattinger', - author_email='crucialfelix@gmail.com', - url='https://github.com/crucialfelix/django-ajax-selects/', - packages=['ajax_select'], + name="django-ajax-selects", + version="1.8.0", + description="Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete.", + author="Chris Sattinger", + author_email="crucialfelix@gmail.com", + url="https://github.com/crucialfelix/django-ajax-selects/", + packages=["ajax_select"], package_data={ - 'ajax_select': - [ - '*.py', - '*.txt', - '*.md', - 'static/ajax_select/css/*', - 'static/ajax_select/images/*', - 'static/ajax_select/js/*', - 'templates/ajax_select/*.html' + "ajax_select": [ + "*.py", + "*.txt", + "*.md", + "static/ajax_select/css/*", + "static/ajax_select/images/*", + "static/ajax_select/js/*", + "templates/ajax_select/*.html", ] }, include_package_data=True, @@ -30,7 +29,7 @@ "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Development Status :: 5 - Production/Stable", - 'Environment :: Web Environment', + "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", @@ -50,7 +49,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.8, <=2.1 -- Python >=2.7, <=3.7 -""" +- Django >=1.8, <3.0 +- Python >=2.7, >=3.5 +""", ) diff --git a/tests/admin.py b/tests/admin.py index bd22e5a63e..4196858e7c 100644 --- a/tests/admin.py +++ b/tests/admin.py @@ -1,5 +1,5 @@ - from django.contrib import admin + from ajax_select.admin import AjaxSelectAdmin, AjaxSelectAdminTabularInline from tests.models import Author, Book, Person from tests.test_integration import BookForm @@ -11,7 +11,6 @@ class BookAdmin(AjaxSelectAdmin): class BookInline(AjaxSelectAdminTabularInline): - model = Book form = BookForm extra = 2 @@ -19,7 +18,6 @@ class BookInline(AjaxSelectAdminTabularInline): @admin.register(Author) class AuthorAdmin(AjaxSelectAdmin): - inlines = [ BookInline ] diff --git a/tests/lookups.py b/tests/lookups.py index 3cf2063a80..ecd8919930 100644 --- a/tests/lookups.py +++ b/tests/lookups.py @@ -7,13 +7,11 @@ from django.utils.html import escape import ajax_select - from tests.models import Author, Person, PersonWithTitle @ajax_select.register('person') class PersonLookup(ajax_select.LookupChannel): - model = Person def get_query(self, q, request): @@ -31,7 +29,6 @@ def format_item_display(self, obj): @ajax_select.register('person-with-title') class PersonWithTitleLookup(ajax_select.LookupChannel): - model = PersonWithTitle def get_query(self, q, request): @@ -43,7 +40,6 @@ def get_result(self, obj): @ajax_select.register('user') class UserLookup(ajax_select.LookupChannel): - """ Test if you can unset a lookup provided by a third-party application. In this case it exposes User without any auth checking @@ -68,5 +64,4 @@ def get_query(self, q, request): @ajax_select.register('author') class AuthorLookup(ajax_select.LookupChannel): - model = Author diff --git a/tests/models.py b/tests/models.py index 509d4a781f..cb64e7880a 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,9 +1,7 @@ - from django.db import models class Person(models.Model): - name = models.CharField(max_length=50) email = models.EmailField(null=True, blank=True) @@ -12,7 +10,6 @@ class Meta: class PersonWithTitle(Person): - """ Testing an inherited model (multi-table) """ @@ -24,7 +21,6 @@ class Meta: class Author(models.Model): - name = models.CharField(max_length=50) class Meta: @@ -32,7 +28,6 @@ class Meta: class Book(models.Model): - """ Book has no admin, its an inline in the Author admin""" author = models.ForeignKey(Author, null=True, on_delete=models.CASCADE) diff --git a/tests/other_lookups.py b/tests/other_lookups.py index 5ccd740c50..3d7fb8651b 100644 --- a/tests/other_lookups.py +++ b/tests/other_lookups.py @@ -5,5 +5,4 @@ class BookLookup(ajax_select.LookupChannel): - model = Book diff --git a/tests/test_fields.py b/tests/test_fields.py index 412401abb3..41821d568f 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,4 +1,5 @@ from django.test import TestCase + from ajax_select import fields from tests.models import Book diff --git a/tests/test_integration.py b/tests/test_integration.py index d7bbcc9bf1..22cc0f89e1 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -6,13 +6,15 @@ should be unit tested in test_fields.py """ from __future__ import unicode_literals + import django -from django.forms.models import ModelForm -from django.test import TestCase, Client from django.contrib.auth.models import User +from django.forms.models import ModelForm +from django.test import Client, TestCase -from tests.models import Book, Author, Person from ajax_select import fields +from tests.models import Author, Book, Person + try: from django.urls import reverse except ImportError: @@ -26,7 +28,6 @@ class BookForm(ModelForm): - class Meta: model = Book fields = ['name', 'author', 'mentions_persons'] @@ -147,7 +148,6 @@ def setUp(self): class TestBookAdmin(TestAdmin): - """ Test the admins in tests/admin.py """ @@ -170,7 +170,6 @@ def test_get_blank(self): class TestAuthorAdmin(TestAdmin): - """ Test an admin with inlines """ diff --git a/tests/test_lookups.py b/tests/test_lookups.py index d17e89dfc8..17ca1a4a29 100644 --- a/tests/test_lookups.py +++ b/tests/test_lookups.py @@ -1,4 +1,3 @@ - from django.contrib.auth.models import User from django.test import TestCase diff --git a/tests/test_registry.py b/tests/test_registry.py index 86e1d5719f..186bfaee1d 100644 --- a/tests/test_registry.py +++ b/tests/test_registry.py @@ -1,5 +1,5 @@ - from django.test import TestCase + import ajax_select @@ -22,7 +22,7 @@ def test_unsetting_a_channel(self): """settings can unset a channel that was specified in a lookups.py""" # self.assertFalse(ajax_select.registry.is_registered('user')) self.assertFalse( - ajax_select.registry.is_registered('was-never-a-channel')) + ajax_select.registry.is_registered('was-never-a-channel')) # def test_reimporting_lookup(self): # """ diff --git a/tests/test_views.py b/tests/test_views.py index 44931b39bb..144ad51fe7 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,14 +1,13 @@ - -from django.test import TestCase from django.contrib.auth.models import User from django.test import Client +from django.test import TestCase class TestViews(TestCase): def setUp(self): self.user = User.objects.create_superuser(username='admin', - email='email@example.com', - password='password') + email='email@example.com', + password='password') self.client = Client() self.client.login(username='admin', password='password') diff --git a/tests/urls.py b/tests/urls.py index 8627c3e6f0..bb80a61635 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,14 +1,13 @@ - -from django.conf.urls import url, include +from django.conf import settings +from django.conf.urls import include, url from django.conf.urls.static import static from django.contrib import admin -from django.conf import settings -from ajax_select import urls as ajax_select_urls +from ajax_select import urls as ajax_select_urls admin.autodiscover() urlpatterns = [ - url(r'^ajax_lookups/', include(ajax_select_urls)), - url(r'^admin/', admin.site.urls), -] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + url(r'^ajax_lookups/', include(ajax_select_urls)), + url(r'^admin/', admin.site.urls), + ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/tox.ini b/tox.ini index 3f99a3405e..834538efba 100644 --- a/tox.ini +++ b/tox.ini @@ -6,9 +6,9 @@ ; [tox] envlist = - {py27,py36}-flake8, - {py27,py36}-{dj18,dj19,dj110,dj111} - py36-{dj20,dj21} + {py27,py37}-flake8, + {py27,py37}-{dj18,dj19,dj110,dj111} + py37-{dj20,dj21} [testenv] setenv = @@ -29,7 +29,7 @@ deps = flake8 commands = flake8 ajax_select tests example -[testenv:py36-flake8] +[testenv:py37-flake8] deps = flake8 commands = flake8 ajax_select tests example