Code cleanup -- Added pep8 tests for package.

This commit is contained in:
Johannes Hoppe 2015-03-30 09:25:28 +02:00
parent 18fa453ac0
commit 2cfbfd3325
14 changed files with 199 additions and 157 deletions

View file

@ -33,7 +33,6 @@ matrix:
- python: "3.2"
- python: "3.3"
- python: "3.4"
- python: "pypy"
- python: "pypy3"
install:
- pip install --upgrade pip
@ -43,6 +42,7 @@ install:
- pip install coveralls
- sh -e /etc/init.d/xvfb start
script:
coverage run --source=django_select2 runtests.py
- isort -c -vb -rc ./django_select2
- coverage run --source=django_select2 runtests.py
after_success:
coveralls

View file

@ -1,3 +1,4 @@
# -*- coding:utf-8 -*-
"""
This is a Django_ integration of Select2_.
@ -75,6 +76,7 @@ The view - `Select2View`, exposed here is meant to be used with 'Heavy' fields a
.. _Read more: http://blog.applegrew.com/2012/08/django-select2/
"""
from __future__ import absolute_import, unicode_literals
import logging
logger = logging.getLogger(__name__)
@ -108,13 +110,22 @@ try:
logger.warn("You need not turn on ENABLE_SELECT2_MULTI_PROCESS_SUPPORT when GENERATE_RANDOM_SELECT2_ID is disabled.")
__ENABLE_MULTI_PROCESS_SUPPORT = False
from .widgets import Select2Widget, Select2MultipleWidget, HeavySelect2Widget, HeavySelect2MultipleWidget, \
AutoHeavySelect2Widget, AutoHeavySelect2MultipleWidget, HeavySelect2TagWidget, AutoHeavySelect2TagWidget
from .fields import Select2ChoiceField, Select2MultipleChoiceField, HeavySelect2ChoiceField, \
HeavySelect2MultipleChoiceField, HeavyModelSelect2ChoiceField, HeavyModelSelect2MultipleChoiceField, \
ModelSelect2Field, ModelSelect2MultipleField, AutoSelect2Field, AutoSelect2MultipleField, \
AutoModelSelect2Field, AutoModelSelect2MultipleField, HeavySelect2TagField, AutoSelect2TagField, \
from .widgets import (
Select2Widget, Select2MultipleWidget,
HeavySelect2Widget, HeavySelect2MultipleWidget,
AutoHeavySelect2Widget, AutoHeavySelect2MultipleWidget,
HeavySelect2TagWidget, AutoHeavySelect2TagWidget
) # NOQA
from .fields import (
Select2ChoiceField, Select2MultipleChoiceField,
HeavySelect2ChoiceField, HeavySelect2MultipleChoiceField,
HeavyModelSelect2ChoiceField, HeavyModelSelect2MultipleChoiceField,
ModelSelect2Field, ModelSelect2MultipleField,
AutoSelect2Field, AutoSelect2MultipleField,
AutoModelSelect2Field, AutoModelSelect2MultipleField,
HeavySelect2TagField, AutoSelect2TagField,
HeavyModelSelect2TagField, AutoModelSelect2TagField
) # NOQA
from .views import Select2View, NO_ERR_RESP
if logger.isEnabledFor(logging.DEBUG):
@ -122,4 +133,3 @@ try:
except ImportError:
if logger.isEnabledFor(logging.INFO):
logger.info("Django not found.")

View file

@ -1,7 +1,10 @@
from models import KeyMap
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
from .models import KeyMap
class Client(object):
def set(self, key, value):
"""
This method is used to set a new value
@ -11,10 +14,10 @@ class Client(object):
if o is None:
o = KeyMap()
o.key = key
o.value = value
o.save()
def get(self, key):
"""
This method is used to retrieve a value

View file

@ -1,9 +1,29 @@
# -*- coding:utf-8 -*-
"""
Contains all the Django fields for Select2.
"""
from __future__ import absolute_import, unicode_literals
import copy
import logging
from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.forms.models import ModelChoiceIterator
from django.utils.encoding import force_unicode, smart_unicode
from django.utils.translation import ugettext_lazy as _
from . import util
from .util import extract_some_key_val
from .views import NO_ERR_RESP
from .widgets import AutoHeavySelect2Mixin # NOQA
from .widgets import (AutoHeavySelect2MultipleWidget,
AutoHeavySelect2TagWidget, AutoHeavySelect2Widget,
HeavySelect2MultipleWidget, HeavySelect2TagWidget,
HeavySelect2Widget, Select2MultipleWidget, Select2Widget)
logger = logging.getLogger(__name__)
@ -41,11 +61,7 @@ class AutoViewFieldMixin(object):
if logger.isEnabledFor(logging.INFO):
logger.info("Registering auto field: %s", name)
from . import util
rf = util.register_field
if logger.isEnabledFor(logging.DEBUG):
rf = util.timer(rf)
id_ = rf(name, self)
self.field_id = id_
@ -70,29 +86,12 @@ class AutoViewFieldMixin(object):
return True
def get_results(self, request, term, page, context):
"See :py:meth:`.views.Select2View.get_results`."
"""See :py:meth:`.views.Select2View.get_results`."""
raise NotImplementedError
import copy
# ## Light general fields ##
from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
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, force_unicode
from .widgets import Select2Widget, Select2MultipleWidget,\
HeavySelect2Widget, HeavySelect2MultipleWidget, AutoHeavySelect2Widget, \
AutoHeavySelect2MultipleWidget, AutoHeavySelect2Mixin, AutoHeavySelect2TagWidget, \
HeavySelect2TagWidget
from .views import NO_ERR_RESP
from .util import extract_some_key_val
### Light general fields ###
class Select2ChoiceField(forms.ChoiceField):
"""
@ -108,7 +107,8 @@ class Select2MultipleChoiceField(forms.MultipleChoiceField):
widget = Select2MultipleWidget
### Model fields related mixins ###
# ## Model fields related mixins ##
class ModelResultJsonMixin(object):
"""
@ -273,9 +273,15 @@ class ModelResultJsonMixin(object):
res = list(qs.filter(*params['or'], **params['and']).distinct())
has_more = False
res = [(getattr(obj, self.to_field_name), self.label_from_instance(obj), self.extra_data_from_instance(obj))
for obj in res]
return (NO_ERR_RESP, has_more, res, )
res = [
(
getattr(obj, self.to_field_name),
self.label_from_instance(obj),
self.extra_data_from_instance(obj)
)
for obj in res
]
return NO_ERR_RESP, has_more, res
class UnhideableQuerysetType(type):
@ -331,6 +337,7 @@ class ChoiceMixin(object):
result._choices = copy.deepcopy(self._choices, memo)
return result
class FilterableModelChoiceIterator(ModelChoiceIterator):
"""
Extends ModelChoiceIterator to add the capability to apply additional
@ -352,6 +359,7 @@ class FilterableModelChoiceIterator(ModelChoiceIterator):
else:
self.queryset = self._original_queryset
class QuerysetChoiceMixin(ChoiceMixin):
"""
Overrides ``choices``' getter to return instance of :py:class:`.FilterableModelChoiceIterator`
@ -381,6 +389,7 @@ class QuerysetChoiceMixin(ChoiceMixin):
result.queryset = result.queryset
return result
class ModelChoiceFieldMixin(QuerysetChoiceMixin):
def __init__(self, *args, **kwargs):
@ -411,7 +420,9 @@ class ModelChoiceFieldMixin(QuerysetChoiceMixin):
def get_pk_field_name(self):
return self.to_field_name or 'pk'
### Slightly altered versions of the Django counterparts with the same name in forms module. ###
# ## Slightly altered versions of the Django counterparts with the same name in forms module. ##
class ModelChoiceField(ModelChoiceFieldMixin, forms.ModelChoiceField):
queryset = property(ModelChoiceFieldMixin._get_queryset, forms.ModelChoiceField._set_queryset)
@ -421,7 +432,7 @@ class ModelMultipleChoiceField(ModelChoiceFieldMixin, forms.ModelMultipleChoiceF
queryset = property(ModelChoiceFieldMixin._get_queryset, forms.ModelMultipleChoiceField._set_queryset)
### Light Fields specialized for Models ###
# ## Light Fields specialized for Models ##
class ModelSelect2Field(ModelChoiceField):
@ -442,7 +453,8 @@ class ModelSelect2MultipleField(ModelMultipleChoiceField):
widget = Select2MultipleWidget
### Heavy fields ###
# ## Heavy fields ##
class HeavySelect2FieldBaseMixin(object):
"""
@ -469,10 +481,6 @@ class HeavySelect2FieldBaseMixin(object):
be raised.
"""
from . import util
if logger.isEnabledFor(logging.DEBUG):
t1 = util.timer_start('HeavySelect2FieldBaseMixin.__init__')
data_view = kwargs.pop('data_view', None)
choices = kwargs.pop('choices', [])
@ -497,16 +505,10 @@ class HeavySelect2FieldBaseMixin(object):
# Widget should have been instantiated by now.
self.widget.field = self
if logger.isEnabledFor(logging.DEBUG):
t2 = util.timer_start('HeavySelect2FieldBaseMixin.__init__:choices initialization')
# ModelChoiceField will set self.choices to ModelChoiceIterator
if choices and not (hasattr(self, 'choices') and isinstance(self.choices, forms.models.ModelChoiceIterator)):
self.choices = choices
if logger.isEnabledFor(logging.DEBUG):
util.timer_end(t2)
util.timer_end(t1)
class HeavyChoiceField(ChoiceMixin, forms.Field):
"""
@ -622,14 +624,15 @@ class HeavyMultipleChoiceField(HeavyChoiceField):
class HeavySelect2ChoiceField(HeavySelect2FieldBaseMixin, HeavyChoiceField):
"Heavy Select2 Choice field."
"""Heavy Select2 Choice field."""
widget = HeavySelect2Widget
class HeavySelect2MultipleChoiceField(HeavySelect2FieldBaseMixin, HeavyMultipleChoiceField):
"Heavy Select2 Multiple Choice field."
"""Heavy Select2 Multiple Choice field."""
widget = HeavySelect2MultipleWidget
class HeavySelect2TagField(HeavySelect2MultipleChoiceField):
"""
Heavy Select2 field for tagging.
@ -663,10 +666,12 @@ class HeavySelect2TagField(HeavySelect2MultipleChoiceField):
"""
raise NotImplementedError
### Heavy field specialized for Models ###
# ## Heavy field specialized for Models ##
class HeavyModelSelect2ChoiceField(HeavySelect2FieldBaseMixin, ModelChoiceField):
"Heavy Select2 Choice field, specialized for Models."
"""Heavy Select2 Choice field, specialized for Models."""
widget = HeavySelect2Widget
def __init__(self, *args, **kwargs):
@ -675,13 +680,14 @@ class HeavyModelSelect2ChoiceField(HeavySelect2FieldBaseMixin, ModelChoiceField)
class HeavyModelSelect2MultipleChoiceField(HeavySelect2FieldBaseMixin, ModelMultipleChoiceField):
"Heavy Select2 Multiple Choice field, specialized for Models."
"""Heavy Select2 Multiple Choice field, specialized for Models."""
widget = HeavySelect2MultipleWidget
def __init__(self, *args, **kwargs):
kwargs.pop('choices', None)
super(HeavyModelSelect2MultipleChoiceField, self).__init__(*args, **kwargs)
class HeavyModelSelect2TagField(HeavySelect2FieldBaseMixin, ModelMultipleChoiceField):
"""
Heavy Select2 field for tagging, specialized for Models.
@ -695,12 +701,12 @@ class HeavyModelSelect2TagField(HeavySelect2FieldBaseMixin, ModelMultipleChoiceF
super(HeavyModelSelect2TagField, self).__init__(*args, **kwargs)
def to_python(self, value):
if value in EMPTY_VALUES:
if value in self.empty_values:
return None
try:
key = self.to_field_name or 'pk'
value = self.queryset.get(**{key: value})
except ValueError, e:
except ValueError:
raise ValidationError(self.error_messages['invalid_choice'])
except self.queryset.model.DoesNotExist:
value = self.create_new_value(value)
@ -769,7 +775,8 @@ class HeavyModelSelect2TagField(HeavySelect2FieldBaseMixin, ModelMultipleChoiceF
"""
raise NotImplementedError
### Heavy general field that uses central AutoView ###
# ## Heavy general field that uses central AutoView ##
class AutoSelect2Field(AutoViewFieldMixin, HeavySelect2ChoiceField):
"""
@ -796,6 +803,7 @@ class AutoSelect2MultipleField(AutoViewFieldMixin, HeavySelect2MultipleChoiceFie
widget = AutoHeavySelect2MultipleWidget
class AutoSelect2TagField(AutoViewFieldMixin, HeavySelect2TagField):
"""
Auto Heavy Select2 field for tagging.
@ -808,7 +816,9 @@ class AutoSelect2TagField(AutoViewFieldMixin, HeavySelect2TagField):
widget = AutoHeavySelect2TagWidget
### Heavy field, specialized for Model, that uses central AutoView ###
# ## Heavy field, specialized for Model, that uses central AutoView ##
class AutoModelSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavyModelSelect2ChoiceField):
"""
@ -837,6 +847,7 @@ class AutoModelSelect2MultipleField(ModelResultJsonMixin, AutoViewFieldMixin, He
widget = AutoHeavySelect2MultipleWidget
class AutoModelSelect2TagField(ModelResultJsonMixin, AutoViewFieldMixin, HeavyModelSelect2TagField):
"""
Auto Heavy Select2 field for tagging, specialized for Models.

View file

@ -1,21 +1,26 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
import memcache
class Client(object):
host = ""
server = ""
expiry = 900
def __init__(self, hostname="127.0.0.1", port="11211", expiry=900):
self.host = "%s:%s" % (hostname, port)
self.server = memcache.Client([self.host])
self.expiry = expiry
def set(self, key, value):
"""
This method is used to set a new value
in the memcache server.
"""
self.server.set(self.normalize_key(key), value, self.expiry)
def get(self, key):
"""
This method is used to retrieve a value

View file

@ -1,14 +1,21 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
class Client(object):
cache = None
db = None
def __init__(self, hostname="127.0.0.1", port="11211", expiry=900):
if hostname and port:
import memcache_client
self.cache = memcache_client.Client(hostname, port, expiry)
import db_client
self.db = db_client.Client()
def set(self, key, value):
"""
This method is used to set a new value
@ -17,7 +24,7 @@ class Client(object):
self.db.set(key, value)
if self.cache:
self.cache.set(key, value)
def get(self, key):
"""
This method is used to retrieve a value

View file

@ -1,9 +1,15 @@
from django.db import models
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django.db import models
from django.utils.encoding import force_text, python_2_unicode_compatible
@python_2_unicode_compatible
class KeyMap(models.Model):
key = models.CharField(max_length=40, unique=True)
value = models.CharField(max_length=100)
accessed_on = models.DateTimeField(auto_now_add=True, auto_now=True)
def __unicode__(self):
return unicode("%s => %s" % (self.key, self.value))
def __str__(self):
return force_text("%s => %s" % (self.key, self.value))

View file

@ -1,32 +1,34 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django import template
from django_select2.widgets import HeavySelect2Widget, Select2Widget
register = template.Library()
from ..widgets import HeavySelect2Widget, Select2Widget
__proxy_widget = HeavySelect2Widget(data_view="xyz")
__proxy_light_widget = Select2Widget()
@register.simple_tag(name='import_django_select2_js')
def import_js(light=0):
if light:
return u'\n'.join(__proxy_light_widget.media.render_js())
else:
return u'\n'.join(__proxy_widget.media.render_js())
if light:
return u'\n'.join(__proxy_light_widget.media.render_js())
else:
return u'\n'.join(__proxy_widget.media.render_js())
@register.simple_tag(name='import_django_select2_css')
def import_css(light=0):
if light:
return u'\n'.join(__proxy_light_widget.media.render_css())
else:
return u'\n'.join(__proxy_widget.media.render_css())
if light:
return u'\n'.join(__proxy_light_widget.media.render_css())
else:
return u'\n'.join(__proxy_widget.media.render_css())
@register.simple_tag(name='import_django_select2_js_css')
def import_all(light=0):
if light:
return __proxy_light_widget.media.render()
else:
return __proxy_widget.media.render()
if light:
return __proxy_light_widget.media.render()
else:
return __proxy_widget.media.render()

View file

@ -1,7 +1,12 @@
from django.conf.urls import *
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django.conf.urls import patterns, url
from .views import AutoResponseView
urlpatterns = patterns("",
url(r"^fields/auto.json$", AutoResponseView.as_view(), name="django_select2_central_json"),
urlpatterns = patterns(
"",
url(r"^fields/auto.json$",
AutoResponseView.as_view(), name="django_select2_central_json"),
)

View file

@ -1,11 +1,18 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
import datetime
import hashlib
import logging
import re
import threading
import types
from django.utils.encoding import force_unicode
from . import __ENABLE_MULTI_PROCESS_SUPPORT as ENABLE_MULTI_PROCESS_SUPPORT
from . import __GENERATE_RANDOM_ID as GENERATE_RANDOM_ID
from . import __MEMCACHE_HOST as MEMCACHE_HOST
from . import __MEMCACHE_PORT as MEMCACHE_PORT
from . import __MEMCACHE_TTL as MEMCACHE_TTL
from . import __SECRET_SALT as SECRET_SALT
logger = logging.getLogger(__name__)
@ -30,15 +37,11 @@ def extract_some_key_val(dct, keys):
return edct
### Auto view helper utils ###
# ## Auto view helper utils ##
from . import __ENABLE_MULTI_PROCESS_SUPPORT as ENABLE_MULTI_PROCESS_SUPPORT, \
__MEMCACHE_HOST as MEMCACHE_HOST, __MEMCACHE_PORT as MEMCACHE_PORT, __MEMCACHE_TTL as MEMCACHE_TTL
from . import __GENERATE_RANDOM_ID as GENERATE_RANDOM_ID, __SECRET_SALT as SECRET_SALT
def synchronized(f):
"Decorator to synchronize multiple calls to a functions."
"""Decorator to synchronize multiple calls to a functions."""
f.__lock__ = threading.Lock()
def synced_f(*args, **kwargs):
@ -53,9 +56,9 @@ __id_store = {}
# Field's key to generated Id mapping.
__field_store = {}
ID_PATTERN = r"[0-9_a-zA-Z.:+\- ]+"
def is_valid_id(val):
"""
Checks if ``val`` is a valid generated Id.
@ -71,10 +74,13 @@ def is_valid_id(val):
else:
return True
if ENABLE_MULTI_PROCESS_SUPPORT:
from memcache_wrapped_db_client import Client
remote_server = Client(MEMCACHE_HOST, str(MEMCACHE_PORT), MEMCACHE_TTL)
@synchronized
def register_field(key, field):
"""
@ -92,7 +98,8 @@ def register_field(key, field):
"""
global __id_store, __field_store
from fields import AutoViewFieldMixin
from .fields import AutoViewFieldMixin
if not isinstance(field, AutoViewFieldMixin):
raise ValueError('Field must extend AutoViewFieldMixin')
@ -110,7 +117,8 @@ def register_field(key, field):
logger.info("Registering new field: %s; With actual id: %s", key, id_)
if ENABLE_MULTI_PROCESS_SUPPORT:
logger.info("Multi process support is enabled. Adding id-key mapping to remote server.")
logger.info(
"Multi process support is enabled. Adding id-key mapping to remote server.")
remote_server.set(id_, key)
else:
id_ = __field_store[key]
@ -144,31 +152,3 @@ def get_field(id_):
else:
logger.error('Unknown id "%s".', id_)
return field
def timer_start(name):
import sys, time
if sys.platform == "win32":
# On Windows, the best timer is time.clock()
default_timer = time.clock
multiplier = 1.0
else:
# On most other platforms the best timer is time.time()
default_timer = time.time
multiplier = 1000.0
return (name, default_timer, multiplier, default_timer())
def timer_end(t):
(name, default_timer, multiplier, timeS) = t
timeE = default_timer()
logger.debug("Time taken by %s: %0.3f ms" % (name, (timeE - timeS) * multiplier))
def timer(f):
def inner(*args, **kwargs):
t = timer_start(f.func_name)
ret = f(*args, **kwargs)
timer_end(t)
return ret
return inner

View file

@ -1,9 +1,11 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import, unicode_literals
import json
from django.http import HttpResponse
from django.views.generic import View
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.http import Http404, HttpResponse
from django.views.generic import View
from .util import get_field, is_valid_id
@ -14,6 +16,7 @@ Equals to 'nil' constant.
Use this in :py:meth:`.Select2View.get_results` to mean no error, instead of hardcoding 'nil' value.
"""
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
@ -31,9 +34,10 @@ class JSONResponseMixin(object):
)
def convert_context_to_json(self, context):
"Convert the context dictionary into a JSON object"
"""Convert the context dictionary into a JSON object"""
return json.dumps(context)
class Select2View(JSONResponseMixin, View):
"""
Base view which is designed to respond with JSON to Ajax queries from heavy widgets/fields.
@ -98,7 +102,7 @@ class Select2View(JSONResponseMixin, View):
if err == NO_ERR_RESP:
for result in results:
id_, text = result[:2]
if len(result)>2:
if len(result) > 2:
extra_data = result[2]
else:
extra_data = {}
@ -190,5 +194,3 @@ class AutoResponseView(Select2View):
field = request.__django_select2_local
del request.__django_select2_local
return field.get_results(request, term, page, context)

View file

@ -1,18 +1,21 @@
# -*- coding:utf-8 -*-
"""
Contains all the Django widgets for Select2.
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from itertools import chain
import re
import util
from itertools import chain
from django import forms
from django.core.urlresolvers import reverse
from django.core.validators import EMPTY_VALUES
from django.utils.datastructures import MergeDict, MultiValueDict
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
from django.utils.datastructures import MultiValueDict, MergeDict
from django.utils.six import text_type
from . import __RENDER_SELECT2_STATICS as RENDER_SELECT2_STATICS
@ -26,6 +29,7 @@ def get_select2_js_libs():
else:
return ('js/select2.min.js', )
def get_select2_heavy_js_libs():
libs = get_select2_js_libs()
@ -35,6 +39,7 @@ def get_select2_heavy_js_libs():
else:
return libs + ('js/heavy_data.min.js', )
def get_select2_css_libs(light=False):
from django.conf import settings
from . import __BOOTSTRAP
@ -61,7 +66,9 @@ def get_select2_css_libs(light=False):
else:
return ('css/all.min.css',)
### Light mixin and widgets ###
# ## Light mixin and widgets ##
class Select2Mixin(object):
"""
@ -228,24 +235,18 @@ class Select2Mixin(object):
:return: The rendered markup.
:rtype: :py:obj:`unicode`
"""
if logger.isEnabledFor(logging.DEBUG):
t1 = util.timer_start('Select2Mixin.render')
args = [name, value, attrs]
if choices:
args.append(choices)
s = unicode(super(Select2Mixin, self).render(*args)) # Thanks to @ouhouhsami Issue#1
s = text_type(super(Select2Mixin, self).render(*args)) # Thanks to @ouhouhsami Issue#1
if RENDER_SELECT2_STATICS:
s += self.media.render()
final_attrs = self.build_attrs(attrs)
id_ = final_attrs.get('id', None)
s += self.render_js_code(id_, name, value, attrs, choices)
if logger.isEnabledFor(logging.DEBUG):
util.timer_end(t1)
logger.debug("Generated widget code:-\n%s", s)
return mark_safe(s)
class Media:
@ -268,10 +269,10 @@ class Select2Widget(Select2Mixin, forms.Select):
def render_options(self, choices, selected_choices):
all_choices = chain(self.choices, choices)
if not self.is_required and \
len([value for value, txt in all_choices if value == '']) == 0: # Checking if list already has empty choice
# as in the case of Model based Light fields.
if not self.is_required \
and len([value for value, txt in all_choices if value == '']) == 0:
# Checking if list already has empty choice
# as in the case of Model based Light fields.
choices = list(choices)
choices.append(('', '', )) # Adding an empty choice
return super(Select2Widget, self).render_options(choices, selected_choices)
@ -295,7 +296,9 @@ class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
self.options.pop('minimumResultsForSearch', None)
### Specialized Multiple Hidden Input Widget ###
# ## Specialized Multiple Hidden Input Widget ##
class MultipleSelect2HiddenInput(forms.TextInput):
"""
Multiple hidden input for Select2.
@ -338,7 +341,9 @@ class MultipleSelect2HiddenInput(forms.TextInput):
data_set = set([force_unicode(value) for value in data])
return data_set != initial_set
### Heavy mixins and widgets ###
# ## Heavy mixins and widgets ##
class HeavySelect2Mixin(Select2Mixin):
"""
@ -617,7 +622,8 @@ class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
return js
### Auto Heavy widgets ###
# ## Auto Heavy widgets ##
class AutoHeavySelect2Mixin(object):
"""
@ -660,14 +666,15 @@ class AutoHeavySelect2Mixin(object):
class AutoHeavySelect2Widget(AutoHeavySelect2Mixin, HeavySelect2Widget):
"Auto version of :py:class:`.HeavySelect2Widget`"
"""Auto version of :py:class:`.HeavySelect2Widget`"""
pass
class AutoHeavySelect2MultipleWidget(AutoHeavySelect2Mixin, HeavySelect2MultipleWidget):
"Auto version of :py:class:`.HeavySelect2MultipleWidget`"
"""Auto version of :py:class:`.HeavySelect2MultipleWidget`"""
pass
class AutoHeavySelect2TagWidget(AutoHeavySelect2Mixin, HeavySelect2TagWidget):
"Auto version of :py:class:`.HeavySelect2TagWidget`"
"""Auto version of :py:class:`.HeavySelect2TagWidget`"""
pass

View file

@ -1,5 +1,8 @@
[pytest]
norecursedirs=env testapp django_select2 docs
norecursedirs=env testapp docs
addopts = --tb=short --pep8 --flakes -rxs
pep8maxlinelength=139
pep8ignore=
flakes-ignore=
django_select2/__init__.py UnusedImport
django_select2/fields.py UnusedImport

View file

@ -3,4 +3,5 @@ pytest-pep8
pytest-flakes
pytest-django
selenium
model_mommy
model_mommy
isort