Skip to content

Commit

Permalink
Merge branch 'release/1.6.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
crucialfelix committed May 17, 2017
2 parents 3493acd + 69a8cbb commit 0e03828
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 97 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ example/ajax_select
example/ajax_selects_example_db
dist
MANIFEST
build
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ clean-pyc:
find . -name '*~' -exec rm -f {} +

lint:
flake8 .
flake8 ajax_select tests example

test:
tox
Expand Down
2 changes: 1 addition & 1 deletion ajax_select/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""JQuery-Ajax Autocomplete fields for Django Forms."""
__version__ = "1.5.2"
__version__ = "1.6.0"
__author__ = "crucialfelix"
__contact__ = "[email protected]"
__homepage__ = "https://github.com/crucialfelix/django-ajax-selects/"
Expand Down
66 changes: 41 additions & 25 deletions ajax_select/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand All @@ -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)

Expand All @@ -61,7 +63,10 @@ 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)
final_attrs.update(attrs or {})
final_attrs.pop('required', None)
self.html_id = final_attrs.pop('id', name)

current_repr = ''
Expand Down Expand Up @@ -131,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:
Expand All @@ -149,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)

Expand All @@ -179,7 +187,9 @@ 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)
Expand Down Expand Up @@ -229,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

Expand All @@ -245,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):
Expand Down Expand Up @@ -298,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)
Expand All @@ -325,9 +338,10 @@ def __init__(self, channel, *args, **kwargs):
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:
Expand All @@ -352,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

Expand All @@ -375,7 +390,7 @@ def __init__(self, channel, *args, **kwargs):
super(AutoCompleteField, self).__init__(*args, **defaults)


####################################################################################
###############################################################################

def _check_can_add(self, user, related_model):
"""
Expand All @@ -402,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)):
Expand Down
58 changes: 33 additions & 25 deletions ajax_select/lookup_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -78,29 +86,28 @@ 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]

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.
Expand All @@ -116,14 +123,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)
Expand Down
6 changes: 6 additions & 0 deletions docs/source/Release-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=====

Expand Down
2 changes: 1 addition & 1 deletion example/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 0e03828

Please sign in to comment.