Skip to content

Commit

Permalink
Merge branch 'release/1.6.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
crucialfelix committed Sep 9, 2017
2 parents 0e03828 + 1311472 commit 053b40c
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 42 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,16 @@
# Change Log

## [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)

**Fixed bugs:**

- Fix Lookup.get\_objects for inherited models [\#212](https://github.com/crucialfelix/django-ajax-selects/pull/212) ([crucialfelix](https://github.com/crucialfelix))

**Merged pull requests:**

- Use DjangoJSONEncoder - which handles additional django types [\#206](https://github.com/crucialfelix/django-ajax-selects/pull/206) ([mzdeb](https://github.com/mzdeb))

## [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)

Expand Down
2 changes: 1 addition & 1 deletion ajax_select/__init__.py
@@ -1,5 +1,5 @@
"""JQuery-Ajax Autocomplete fields for Django Forms."""
__version__ = "1.6.0"
__version__ = "1.6.1"
__author__ = "crucialfelix"
__contact__ = "crucialfelix@gmail.com"
__homepage__ = "https://github.com/crucialfelix/django-ajax-selects/"
Expand Down
50 changes: 34 additions & 16 deletions ajax_select/fields.py
@@ -1,17 +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.db.models.query import QuerySet
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Model
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 _

from ajax_select.registry import registry

try:
from django.urls import reverse
except ImportError:
Expand Down Expand Up @@ -96,7 +101,8 @@ def render(self, name, value, attrs=None):
'func_slug': self.html_id.replace("-", ""),
'add_link': self.add_link,
}
context.update(make_plugin_options(lookup, self.channel, self.plugin_options, initial))
context.update(
make_plugin_options(lookup, self.channel, self.plugin_options, initial))
templates = (
'ajax_select/autocompleteselect_%s.html' % self.channel,
'ajax_select/autocompleteselect.html')
Expand Down Expand Up @@ -127,7 +133,8 @@ def __init__(self, channel, *args, **kwargs):
)
widget_kwargs.update(kwargs.pop('widget_options', {}))
kwargs["widget"] = AutoCompleteSelectWidget(**widget_kwargs)
super(AutoCompleteSelectField, self).__init__(max_length=255, *args, **kwargs)
super(AutoCompleteSelectField, self).__init__(
max_length=255, *args, **kwargs)

def clean(self, value):
if value:
Expand All @@ -138,7 +145,8 @@ def clean(self, value):
# or your channel is faulty
# 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))
raise forms.ValidationError(
"%s cannot find object: %s" % (lookup, value))
return objs[0]
else:
if self.required:
Expand Down Expand Up @@ -194,10 +202,11 @@ def render(self, name, value, attrs=None):

lookup = registry.get(self.channel)

if isinstance(value, QuerySet):
objects = value
values = list(value)
if all([isinstance(v, Model) for v in values]):
objects = values
else:
objects = lookup.get_objects(value)
objects = lookup.get_objects(values)

current_ids = pack_ids([obj.pk for obj in objects])

Expand All @@ -217,15 +226,18 @@ def render(self, name, value, attrs=None):
'html_id': self.html_id,
'current': value,
'current_ids': current_ids,
'current_reprs': mark_safe(json.dumps(initial)),
'current_reprs': mark_safe(
json.dumps(initial, cls=DjangoJSONEncoder)
),
'help_text': help_text,
'extra_attrs': mark_safe(flatatt(final_attrs)),
'func_slug': self.html_id.replace("-", ""),
'add_link': self.add_link,
}
context.update(make_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')
'ajax_select/autocompleteselectmultiple.html')
out = render_to_string(templates, context)
return mark_safe(out)

Expand Down Expand Up @@ -269,7 +281,8 @@ def __init__(self, channel, *args, **kwargs):
dh = 'Hold down "Control", or "Command" on a Mac, to select more than one.'
django_default_help = _(dh).translate(settings.LANGUAGE_CODE)
if django_default_help in translated:
cleaned_help = translated.replace(django_default_help, '').strip()
cleaned_help = translated.replace(
django_default_help, '').strip()
# probably will not show up in translations
if cleaned_help:
help_text = cleaned_help
Expand Down Expand Up @@ -358,13 +371,15 @@ def render(self, name, value, attrs=None):
'extra_attrs': mark_safe(flatatt(final_attrs)),
'func_slug': self.html_id.replace("-", ""),
}
context.update(make_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))


class AutoCompleteField(forms.CharField):

"""
A CharField that uses an AutoCompleteWidget to lookup matching
and stores the result as plain text.
Expand Down Expand Up @@ -411,7 +426,8 @@ def _check_can_add(self, user, related_model):
if can_add:
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'
self.widget.add_link = reverse(
'admin:%s_%s_add' % (app_label, model)) + '?_popup=1'


def autoselect_fields_check_can_add(form, model, user):
Expand Down Expand Up @@ -441,8 +457,10 @@ def make_plugin_options(lookup, channel_name, widget_plugin_options, initial):
po['html'] = True

return {
'plugin_options': mark_safe(json.dumps(po)),
'data_plugin_options': force_escape(json.dumps(po))
'plugin_options': mark_safe(json.dumps(po, cls=DjangoJSONEncoder)),
'data_plugin_options': force_escape(
json.dumps(po, cls=DjangoJSONEncoder)
)
}


Expand Down
3 changes: 2 additions & 1 deletion ajax_select/lookup_channel.py
Expand Up @@ -94,9 +94,10 @@ def get_objects(self, ids):
Returns:
list: list of Model objects
"""
# Inherited models have a OneToOneField (rather than eg AutoField)
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
pk_type = self.model._meta.pk.rel.field.to_python
else:
pk_type = self.model._meta.pk.to_python

Expand Down
22 changes: 11 additions & 11 deletions setup.py
Expand Up @@ -9,23 +9,23 @@

setup(
name='django-ajax-selects',
version='1.6.0',
version='1.6.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'],
package_data={'ajax_select':
[
'*.py',
'*.txt',
'*.md',
'static/ajax_select/css/*',
'static/ajax_select/images/*',
'static/ajax_select/js/*',
'templates/ajax_select/*.html'
]
},
[
'*.py',
'*.txt',
'*.md',
'static/ajax_select/css/*',
'static/ajax_select/images/*',
'static/ajax_select/js/*',
'templates/ajax_select/*.html'
]
},
include_package_data=True,
zip_safe=False,
license="MIT",
Expand Down
18 changes: 16 additions & 2 deletions tests/lookups.py
Expand Up @@ -3,11 +3,13 @@
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, Author
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):
Expand All @@ -27,6 +29,18 @@ def format_item_display(self, obj):
return "%s<div><i>%s</i></div>" % (escape(obj.name), escape(obj.email))


@ajax_select.register('person-with-title')
class PersonWithTitleLookup(ajax_select.LookupChannel):

model = PersonWithTitle

def get_query(self, q, request):
return self.model.objects.filter(title__icontains=q)

def get_result(self, obj):
return "{} {}".format(obj.name, obj.title)


@ajax_select.register('user')
class UserLookup(ajax_select.LookupChannel):

Expand Down
12 changes: 12 additions & 0 deletions tests/models.py
Expand Up @@ -11,6 +11,18 @@ class Meta:
app_label = 'tests'


class PersonWithTitle(Person):

"""
Testing an inherited model (multi-table)
"""

title = models.CharField(max_length=150)

class Meta:
app_label = 'tests'


class Author(models.Model):

name = models.CharField(max_length=50)
Expand Down
29 changes: 23 additions & 6 deletions tests/test_lookups.py
@@ -1,21 +1,38 @@

from django.test import TestCase
from django.contrib.auth.models import User
from .lookups import UserLookup
from django.test import TestCase

from tests.lookups import PersonWithTitleLookup, UserLookup
from tests.models import PersonWithTitle


class TestLookups(TestCase):

def test_get_objects(self):
user1 = User.objects.create(username='user1',
email='user1@example.com',
password='password')
email='user1@example.com',
password='password')
user2 = User.objects.create(username='user2',
email='user2@example.com',
password='password')
email='user2@example.com',
password='password')
lookup = UserLookup()
# deliberately asking for them backwards:
users = lookup.get_objects([user2.id, user1.id])
self.assertEqual(len(users), 2)
# to make sure they come back in the order requested
u2, u1 = users
self.assertEqual(u1, user1)
self.assertEqual(u2, user2)

def test_get_objects_inherited_model(self):
"""
Tests that get_objects works with inherited models
"""
one = PersonWithTitle.objects.create(name='one', title='The One')
two = PersonWithTitle.objects.create(name='two', title='The Other')
lookup = PersonWithTitleLookup()
users = lookup.get_objects([one.id, two.id])
self.assertEqual(len(users), 2)
u1, u2 = users
self.assertEqual(u1, one)
self.assertEqual(u2, two)
10 changes: 5 additions & 5 deletions tox.ini
Expand Up @@ -16,11 +16,11 @@ setenv =
PYTHONPATH = {toxinidir}:{toxinidir}/ajax_select:{toxinidir}/tests
commands = django-admin.py test tests
deps =
dj17: Django>=1.7,<1.8
dj18: Django>=1.8,<1.9
dj19: Django>=1.9,<1.10
dj110: Django>=1.10,<1.11
dj111: Django>=1.11,<1.12
dj17: Django>=1.7.11,<1.8
dj18: Django>=1.8.18,<1.9
dj19: Django>=1.9.13,<1.10
dj110: Django>=1.10.8,<1.11
dj111: Django>=1.11.5,<1.12
; djmaster: https://github.com/django/django/zipball/master

[testenv:py27-flake8]
Expand Down

0 comments on commit 053b40c

Please sign in to comment.