diff --git a/django_select2/fields.py b/django_select2/fields.py index e5509eb..1d27479 100644 --- a/django_select2/fields.py +++ b/django_select2/fields.py @@ -6,6 +6,7 @@ import logging logger = logging.getLogger(__name__) + class AutoViewFieldMixin(object): """ Registers itself with AutoResponseView. @@ -82,7 +83,6 @@ from django.forms.models import ModelChoiceIterator from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode -from django.core.validators import EMPTY_VALUES from .widgets import Select2Widget, Select2MultipleWidget,\ HeavySelect2Widget, HeavySelect2MultipleWidget, AutoHeavySelect2Widget, \ @@ -90,6 +90,7 @@ from .widgets import Select2Widget, Select2MultipleWidget,\ from .views import NO_ERR_RESP from .util import extract_some_key_val + ### Light general fields ### class Select2ChoiceField(forms.ChoiceField): @@ -134,7 +135,7 @@ class ModelResultJsonMixin(object): value. (Default is ``pk``, i.e. the id field of the model) :type to_field_name: :py:obj:`str` """ - if self.queryset is None and not kwargs.has_key('queryset'): + if self.queryset is None and not 'queryset' in kwargs: raise ValueError('queryset is required.') if not self.search_fields: @@ -201,7 +202,7 @@ class ModelResultJsonMixin(object): | Assume, ``search_term == 'John'`` | ``self.search_fields == ['first_name__icontains', 'last_name__icontains']`` - + So, the prepared query would be:: { @@ -211,7 +212,7 @@ class ModelResultJsonMixin(object): 'and': {} } :rtype: :py:obj:`dict` - """ + """ q = None for field in search_fields: kwargs = {} @@ -220,7 +221,7 @@ class ModelResultJsonMixin(object): q = Q(**kwargs) else: q = q | Q(**kwargs) - return {'or': [q], 'and': {},} + return {'or': [q], 'and': {}} def get_results(self, request, term, page, context): """ @@ -233,7 +234,7 @@ class ModelResultJsonMixin(object): if self.max_results: min_ = (page - 1) * self.max_results - max_ = min_ + self.max_results + 1 # fetching one extra row to check if it has more rows. + max_ = min_ + self.max_results + 1 # fetching one extra row to check if it has more rows. res = list(qs.filter(*params['or'], **params['and'])[min_:max_]) has_more = len(res) == (max_ - min_) if has_more: @@ -242,7 +243,7 @@ class ModelResultJsonMixin(object): res = list(qs.filter(*params['or'], **params['and'])) has_more = False - res = [ (getattr(obj, self.to_field_name), self.label_from_instance(obj), ) for obj in res ] + res = [(getattr(obj, self.to_field_name), self.label_from_instance(obj), ) for obj in res] return (NO_ERR_RESP, has_more, res, ) @@ -263,7 +264,7 @@ class UnhideableQuerysetType(type): # class variable named - queryset, which will # effectively hide the queryset declared in this # mixin. - dct.pop('queryset') # Throwing away the sub-class queryset + dct.pop('queryset') # Throwing away the sub-class queryset dct['_subclass_queryset'] = _q return type.__new__(cls, name, bases, dct) @@ -331,10 +332,11 @@ class ModelChoiceFieldMixin(object): 'empty_label', 'cache_choices', 'required', 'label', 'initial', 'help_text', ]) kargs['widget'] = kwargs.pop('widget', getattr(self, 'widget', None)) - kargs['to_field_name'] = kwargs.pop('to_field_name', 'pk') + kargs['to_field_name'] = kwargs.pop('to_field_name', 'pk') - if hasattr(self, '_choices'): # If it exists then probably it is set by HeavySelect2FieldBase. - # We are not gonna use that anyway. + # If it exists then probably it is set by HeavySelect2FieldBase. + # We are not gonna use that anyway. + if hasattr(self, '_choices'): del self._choices super(ModelChoiceFieldMixin, self).__init__(queryset, **kargs) @@ -357,9 +359,10 @@ class ModelMultipleChoiceField(ModelChoiceFieldMixin, forms.ModelMultipleChoiceF queryset = property(ModelChoiceFieldMixin._get_queryset, forms.ModelMultipleChoiceField._set_queryset) -### Light Fileds specialized for Models ### +### Light Fields specialized for Models ### -class ModelSelect2Field(ModelChoiceField) : + +class ModelSelect2Field(ModelChoiceField): """ Light Select2 field, specialized for Models. @@ -368,7 +371,7 @@ class ModelSelect2Field(ModelChoiceField) : widget = Select2Widget -class ModelSelect2MultipleField(ModelMultipleChoiceField) : +class ModelSelect2MultipleField(ModelMultipleChoiceField): """ Light multiple-value Select2 field, specialized for Models. @@ -426,7 +429,8 @@ class HeavySelect2FieldBaseMixin(object): if hasattr(self, 'field_id'): self.widget.field_id = self.field_id - if not choices and hasattr(self, 'choices'): # ModelChoiceField will set this to ModelChoiceIterator + # ModelChoiceField will set this to ModelChoiceIterator + if not choices and hasattr(self, 'choices'): choices = self.choices self.choices = choices @@ -494,7 +498,7 @@ class HeavyChoiceField(ChoiceMixin, forms.Field): try: value = self.coerce_value(value) self.validate_value(value) - except Exception, e: + except Exception: logger.exception("Exception while trying to get label for value") return None return self.get_val_txt(value) @@ -512,6 +516,7 @@ class HeavyChoiceField(ChoiceMixin, forms.Field): """ return None + class HeavyMultipleChoiceField(HeavyChoiceField): """ Reimplements :py:class:`django.forms.TypedMultipleChoiceField` in a way which suites the use of big data. @@ -620,8 +625,9 @@ class AutoModelSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavyModel This needs to be subclassed. The first instance of a class (sub-class) is used to serve all incoming json query requests for that type (class). """ - __metaclass__ = UnhideableQuerysetType # Makes sure that user defined queryset class variable is replaced by - # queryset property (as it is needed by super classes). + # ModelChoiceField will set this to ModelChoiceIterator + # queryset property (as it is needed by super classes). + __metaclass__ = UnhideableQuerysetType widget = AutoHeavySelect2Widget @@ -638,8 +644,9 @@ class AutoModelSelect2MultipleField(ModelResultJsonMixin, AutoViewFieldMixin, He This needs to be subclassed. The first instance of a class (sub-class) is used to serve all incoming json query requests for that type (class). """ - __metaclass__ = UnhideableQuerysetType # Makes sure that user defined queryset class variable is replaced by - # queryset property (as it is needed by super classes). + # Makes sure that user defined queryset class variable is replaced by + # queryset property (as it is needed by super classes). + __metaclass__ = UnhideableQuerysetType widget = AutoHeavySelect2MultipleWidget @@ -647,5 +654,3 @@ class AutoModelSelect2MultipleField(ModelResultJsonMixin, AutoViewFieldMixin, He self.data_view = "django_select2_central_json" kwargs['data_view'] = self.data_view super(AutoModelSelect2MultipleField, self).__init__(*args, **kwargs) - - diff --git a/django_select2/templatetags/django_select2_tags.py b/django_select2/templatetags/django_select2_tags.py index d697e3e..bc189ec 100644 --- a/django_select2/templatetags/django_select2_tags.py +++ b/django_select2/templatetags/django_select2_tags.py @@ -1,5 +1,4 @@ from django import template -from django.conf import settings register = template.Library() @@ -7,14 +6,17 @@ from ..widgets import HeavySelect2Widget __proxy_widget = HeavySelect2Widget(data_view="xyz") + @register.simple_tag(name='import_django_select2_js') def import_js(): return u'\n'.join(__proxy_widget.media.render_js()) + @register.simple_tag(name='import_django_select2_css') def import_css(): return u'\n'.join(__proxy_widget.media.render_css()) + @register.simple_tag(name='import_django_select2_js_css') def import_all(): return __proxy_widget.media.render() diff --git a/django_select2/urls.py b/django_select2/urls.py index c3bac3e..e587d05 100644 --- a/django_select2/urls.py +++ b/django_select2/urls.py @@ -3,5 +3,5 @@ from django.conf.urls.defaults import * from .views import AutoResponseView urlpatterns = patterns("", - url(r"^fields/auto.json$", AutoResponseView.as_view(), name="django_select2_central_json"), + url(r"^fields/auto.json$", AutoResponseView.as_view(), name="django_select2_central_json"), ) diff --git a/django_select2/util.py b/django_select2/util.py index 8619568..47c770f 100644 --- a/django_select2/util.py +++ b/django_select2/util.py @@ -1,11 +1,14 @@ -import types +import datetime import logging +import re +import threading +import types -from django.utils.html import escape from django.utils.encoding import force_unicode logger = logging.getLogger(__name__) + class JSVar(unicode): """ A JS variable. @@ -107,7 +110,7 @@ def convert_py_to_js_data(val, id_): elif isinstance(val, JSFunctionInContext): return u"django_select2.runInContextHelper(%s, '%s')" % (val, id_) elif isinstance(val, JSVar): - return val # No quotes here + return val # No quotes here elif isinstance(val, dict): return convert_dict_to_js_map(val, id_) elif isinstance(val, list): @@ -184,11 +187,6 @@ def convert_to_js_string_arr(lst): ### Auto view helper utils ### -import re -import threading -import datetime - - def synchronized(f): "Decorator to synchronize multiple calls to a functions." f.__lock__ = threading.Lock() @@ -200,9 +198,10 @@ def synchronized(f): synced_f.__doc__ = f.__doc__ return synced_f - -__id_store = {} # Generated Id to field instance mapping. -__field_store = {} # Field's key to generated Id mapping. +# Generated Id to field instance mapping. +__id_store = {} +# Field's key to generated Id mapping. +__field_store = {} ID_PATTERN = r"[0-9_a-zA-Z.:+\- ]+" diff --git a/django_select2/widgets.py b/django_select2/widgets.py index 2dc9215..a585e09 100644 --- a/django_select2/widgets.py +++ b/django_select2/widgets.py @@ -18,6 +18,7 @@ from . import __RENDER_SELECT2_STATICS as RENDER_SELECT2_STATICS logger = logging.getLogger(__name__) + def get_select2_js_path(): from django.conf import settings if settings.configured and settings.DEBUG: @@ -48,10 +49,10 @@ class Select2Mixin(object): # For details on these options refer: http://ivaynberg.github.com/select2/#documentation options = { - 'minimumResultsForSearch': 6, # Only applicable for single value select. - 'placeholder': '', # Empty text label - 'allowClear': True, # Not allowed when field is multiple since there each value has a clear button. - 'multiple': False, # Not allowed when attached to 'closeOnSelect': False, } """ @@ -61,7 +62,6 @@ class Select2Mixin(object): .. _Select2: http://ivaynberg.github.com/select2/#documentation. """ - def __init__(self, **kwargs): """ Constructor of the class. @@ -95,7 +95,8 @@ class Select2Mixin(object): :type select2_options: :py:obj:`dict` or None """ - self.options = dict(self.options) # Making an instance specific copy + # Making an instance specific copy + self.options = dict(self.options) self.init_options() select2_options = kwargs.pop('select2_options', None) if select2_options: @@ -126,7 +127,7 @@ class Select2Mixin(object): def set_placeholder(self, val): """ Placeholder is a value which Select2 JS library shows when nothing is selected. This should be string. - + :return: None """ self.options['placeholder'] = val @@ -134,7 +135,7 @@ class Select2Mixin(object): def get_options(self): """ :return: Dictionary of options to be passed to Select2 JS. - + :rtype: :py:obj:`dict` """ options = dict(self.options) @@ -145,7 +146,7 @@ class Select2Mixin(object): def render_select2_options_code(self, options, id_): """ Renders options for Select2 JS. - + :return: The rendered JS code. :rtype: :py:obj:`unicode` """ @@ -154,7 +155,7 @@ class Select2Mixin(object): def render_js_code(self, id_, *args): """ Renders the ``