mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-04-21 05:31:00 +00:00
Multi value tests added and more fixes.
* Fixed infinite recursion causing Python crash bug. * Multi-value is working. * Tested added for multi-value field.
This commit is contained in:
parent
ce5b42d531
commit
f3569bcea2
11 changed files with 123 additions and 68 deletions
|
|
@ -5,5 +5,5 @@ if settings.configured:
|
|||
from .widgets import Select2Widget, Select2MultipleWidget, HeavySelect2Widget, HeavySelect2MultipleWidget, AutoHeavySelect2Widget
|
||||
from .fields import Select2ChoiceField, Select2MultipleChoiceField, \
|
||||
HeavySelect2ChoiceField, HeavySelect2MultipleChoiceField, \
|
||||
ModelSelect2Field, AutoSelect2Field, AutoModelSelect2Field #, ModelMultipleSelect2Field
|
||||
ModelSelect2Field, AutoSelect2Field, AutoModelSelect2Field, ModelMultipleSelect2Field
|
||||
from .views import Select2View, NO_ERR_RESP
|
||||
|
|
|
|||
|
|
@ -26,6 +26,17 @@ from django.core.validators import EMPTY_VALUES
|
|||
from .widgets import Select2Widget, Select2MultipleWidget,\
|
||||
HeavySelect2Widget, HeavySelect2MultipleWidget, AutoHeavySelect2Widget
|
||||
from .views import NO_ERR_RESP
|
||||
from .util import extract_some_key_val
|
||||
|
||||
### Light general fields ###
|
||||
|
||||
class Select2ChoiceField(forms.ChoiceField):
|
||||
widget = Select2Widget
|
||||
|
||||
class Select2MultipleChoiceField(forms.MultipleChoiceField):
|
||||
widget = Select2MultipleWidget
|
||||
|
||||
### Model fields related mixins ###
|
||||
|
||||
class ModelResultJsonMixin(object):
|
||||
|
||||
|
|
@ -126,25 +137,20 @@ class QuerysetChoiceMixin(ChoiceMixin):
|
|||
choices = property(_get_choices, ChoiceMixin._set_choices)
|
||||
|
||||
class ModelChoiceFieldMixin(object):
|
||||
#class ModelChoiceField(forms.ModelChoiceField):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
queryset = kwargs.pop('queryset', None)
|
||||
empty_label = kwargs.pop('empty_label', u"---------")
|
||||
cache_choices = kwargs.pop('cache_choices', False)
|
||||
required = kwargs.pop('required', True)
|
||||
widget = kwargs.pop('widget', getattr(self, 'widget', None))
|
||||
label = kwargs.pop('label', None)
|
||||
initial = kwargs.pop('initial', None)
|
||||
help_text = kwargs.pop('help_text', None)
|
||||
to_field_name = kwargs.pop('to_field_name', 'pk')
|
||||
kargs = extract_some_key_val(kwargs, [
|
||||
'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')
|
||||
|
||||
if hasattr(self, '_choices'): # If it exists then probably it is set by HeavySelect2FieldBase.
|
||||
# We are not gonna use that anyway.
|
||||
del self._choices
|
||||
|
||||
super(ModelChoiceField, self).__init__(queryset, empty_label, cache_choices, required,
|
||||
widget, label, initial, help_text, to_field_name)
|
||||
super(ModelChoiceFieldMixin, self).__init__(queryset, **kargs)
|
||||
|
||||
if hasattr(self, 'set_placeholder'):
|
||||
self.widget.set_placeholder(self.empty_label)
|
||||
|
|
@ -153,20 +159,26 @@ class ModelChoiceFieldMixin(object):
|
|||
if hasattr(self, '_queryset'):
|
||||
return self._queryset
|
||||
|
||||
# queryset = property(_get_queryset, forms.ModelChoiceField._set_queryset)
|
||||
### 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)
|
||||
|
||||
#class ModelMultipleChoiceField(ModelChoiceFieldMixin, forms.ModelMultipleChoiceField):
|
||||
# queryset = property(ModelChoiceFieldMixin._get_queryset, forms.ModelMultipleChoiceField._set_queryset)
|
||||
class ModelMultipleChoiceField(ModelChoiceFieldMixin, forms.ModelMultipleChoiceField):
|
||||
queryset = property(ModelChoiceFieldMixin._get_queryset, forms.ModelMultipleChoiceField._set_queryset)
|
||||
|
||||
class Select2ChoiceField(forms.ChoiceField):
|
||||
### Light Fileds specialized for Models ###
|
||||
|
||||
class ModelSelect2Field(ModelChoiceField) :
|
||||
"Light Model Select2 field"
|
||||
widget = Select2Widget
|
||||
|
||||
class Select2MultipleChoiceField(forms.MultipleChoiceField):
|
||||
class ModelMultipleSelect2Field(ModelMultipleChoiceField) :
|
||||
"Light multiple-value Model Select2 field"
|
||||
widget = Select2MultipleWidget
|
||||
|
||||
### Heavy fields ###
|
||||
|
||||
class HeavySelect2FieldBase(ChoiceMixin, forms.Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
data_view = kwargs.pop('data_view', None)
|
||||
|
|
@ -187,11 +199,15 @@ class HeavySelect2ChoiceField(HeavySelect2FieldBase):
|
|||
class HeavySelect2MultipleChoiceField(HeavySelect2FieldBase):
|
||||
widget = HeavySelect2MultipleWidget
|
||||
|
||||
### Heavy field specialized for Models (Single valued) ###
|
||||
|
||||
class HeavyModelSelect2ChoiceField(QuerysetChoiceMixin, HeavySelect2ChoiceField, ModelChoiceField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.pop('choices', None)
|
||||
super(HeavyModelSelect2ChoiceField, self).__init__(*args, **kwargs)
|
||||
|
||||
### Heavy general field that uses central AutoView ###
|
||||
|
||||
class AutoSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavySelect2ChoiceField):
|
||||
"""
|
||||
This needs to be subclassed. The first instance of a class (sub-class) is used to serve all incoming
|
||||
|
|
@ -205,12 +221,15 @@ class AutoSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavySelect2Cho
|
|||
kwargs['data_view'] = self.data_view
|
||||
super(AutoSelect2Field, self).__init__(*args, **kwargs)
|
||||
|
||||
### Heavy field, specialized for Model, that uses central AutoView ###
|
||||
|
||||
class AutoModelSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavyModelSelect2ChoiceField):
|
||||
"""
|
||||
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
|
||||
__metaclass__ = UnhideableQuerysetType # Makes sure that user defined queryset class variable is replaced by
|
||||
# queryset property (as it is needed by super classes).
|
||||
|
||||
widget = AutoHeavySelect2Widget
|
||||
|
||||
|
|
@ -218,11 +237,3 @@ class AutoModelSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavyModel
|
|||
self.data_view = "django_select2_central_json"
|
||||
kwargs['data_view'] = self.data_view
|
||||
super(AutoModelSelect2Field, self).__init__(*args, **kwargs)
|
||||
|
||||
class ModelSelect2Field(ModelChoiceField) :
|
||||
"Light Model Select2 field"
|
||||
widget = Select2Widget
|
||||
|
||||
#class ModelMultipleSelect2Field(ModelMultipleChoiceField) :
|
||||
# "Light multiple-value Model Select2 field"
|
||||
# widget = Select2MultipleWidget
|
||||
|
|
|
|||
|
|
@ -3,4 +3,7 @@
|
|||
}
|
||||
.select2-container {
|
||||
min-width: 150px;
|
||||
}
|
||||
.select2-container.select2-container-multi {
|
||||
width: 300px;
|
||||
}
|
||||
|
|
@ -29,6 +29,14 @@ class JSFunctionInContext(JSVar):
|
|||
"""
|
||||
pass
|
||||
|
||||
def extract_some_key_val(dct, keys):
|
||||
edct = {}
|
||||
for k in keys:
|
||||
v = dct.get(k, None)
|
||||
if v is not None:
|
||||
edct[k] = v
|
||||
return edct
|
||||
|
||||
### Auto view helper utils ###
|
||||
|
||||
import re
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from django.core.urlresolvers import reverse
|
|||
|
||||
from .util import render_js_script, convert_to_js_string_arr, JSVar, JSFunction, JSFunctionInContext
|
||||
|
||||
### Light mixin and widgets ###
|
||||
|
||||
class Select2Mixin(object):
|
||||
# For details on these options refer: http://ivaynberg.github.com/select2/#documentation
|
||||
options = {
|
||||
|
|
@ -96,6 +98,45 @@ class Select2Mixin(object):
|
|||
js = ('js/select2.min.js', )
|
||||
css = {'screen': ('css/select2.css', 'css/extra.css', )}
|
||||
|
||||
class Select2Widget(Select2Mixin, forms.Select):
|
||||
def init_options(self):
|
||||
self.options.pop('multiple', None)
|
||||
|
||||
def render_options(self, choices, selected_choices):
|
||||
if not self.is_required:
|
||||
choices = list(choices)
|
||||
choices.append(('', '', )) # Adding an empty choice
|
||||
return super(Select2Widget, self).render_options(choices, selected_choices)
|
||||
|
||||
class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
|
||||
def init_options(self):
|
||||
self.options.pop('multiple', None)
|
||||
self.options.pop('allowClear', None)
|
||||
self.options.pop('minimumResultsForSearch', None)
|
||||
|
||||
### Specialized Multiple Hidden Input Widget ###
|
||||
class MultipleSelect2HiddenInput(forms.TextInput):
|
||||
"""
|
||||
This is a specialized multiple Hidden Input widget. This includes a special
|
||||
JS component which renders multiple Hidden Input boxes as there are values.
|
||||
So, if user suppose chooses values 1,4,9 then Select2 would would write them
|
||||
to the hidden input. The JS component of this widget will read that value and
|
||||
will render three more hidden input boxes each with values 1, 4 and 9 respectively.
|
||||
They will all share the name of this field, and the name of the primary source
|
||||
hidden input would be removed. This way, when submitted all the selected values
|
||||
would be available was would have been for a <select> multiple field.
|
||||
"""
|
||||
input_type = 'hidden' # We want it hidden but should be treated as if is_hidden is False
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
attrs = self.build_attrs(attrs, multiple='multiple')
|
||||
s = unicode(super(MultipleSelect2HiddenInput, self).render(name, value, attrs, choices))
|
||||
id_ = attrs.get('id', None)
|
||||
if id_:
|
||||
s += render_js_script(u"django_select2.initMultipleHidden($('#%s'));" % id_)
|
||||
return s
|
||||
|
||||
### Heavy mixins and widgets ###
|
||||
|
||||
class HeavySelect2Mixin(Select2Mixin):
|
||||
def __init__(self, **kwargs):
|
||||
self.options = dict(self.options) # Making an instance specific copy
|
||||
|
|
@ -152,38 +193,6 @@ class HeavySelect2Mixin(Select2Mixin):
|
|||
js = ('js/select2.min.js', 'js/heavy_data.js', )
|
||||
css = {'screen': ('css/select2.css', 'css/extra.css', )}
|
||||
|
||||
class MultipleSelect2HiddenInput(forms.TextInput):
|
||||
input_type = 'hidden' # We want it hidden but should be treated as if is_hidden is False
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
attrs = self.build_attrs(attrs, multiple='multiple')
|
||||
s = unicode(super(MultipleSelect2HiddenInput, self).render(name, value, attrs, choices))
|
||||
id_ = attrs.get('id', None)
|
||||
if id_:
|
||||
s += render_js_script(u"django_select2.initMultipleHidden($('#%s'));" % id_)
|
||||
return s
|
||||
|
||||
class AutoHeavySelect2Mixin(HeavySelect2Mixin):
|
||||
def render_inner_js_code(self, id_, *args):
|
||||
js = super(AutoHeavySelect2Mixin, self).render_inner_js_code(id_, *args)
|
||||
js += u"$('#%s').data('field_id', '%s');" % (id_, self.field_id)
|
||||
return js
|
||||
|
||||
class Select2Widget(Select2Mixin, forms.Select):
|
||||
def init_options(self):
|
||||
self.options.pop('multiple', None)
|
||||
|
||||
def render_options(self, choices, selected_choices):
|
||||
if not self.is_required:
|
||||
choices = list(choices)
|
||||
choices.append(('', '', )) # Adding an empty choice
|
||||
return super(Select2Widget, self).render_options(choices, selected_choices)
|
||||
|
||||
class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
|
||||
def init_options(self):
|
||||
self.options.pop('multiple', None)
|
||||
self.options.pop('allowClear', None)
|
||||
self.options.pop('minimumResultsForSearch', None)
|
||||
|
||||
class HeavySelect2Widget(HeavySelect2Mixin, forms.TextInput):
|
||||
input_type = 'hidden' # We want it hidden but should be treated as if is_hidden is False
|
||||
def init_options(self):
|
||||
|
|
@ -202,5 +211,13 @@ class HeavySelect2MultipleWidget(HeavySelect2Mixin, MultipleSelect2HiddenInput):
|
|||
if texts:
|
||||
return render_js_script(u"$('#%s').attr('txt', %s);" % (id_, texts))
|
||||
|
||||
### Auto Heavy widgets ###
|
||||
|
||||
class AutoHeavySelect2Mixin(HeavySelect2Mixin):
|
||||
def render_inner_js_code(self, id_, *args):
|
||||
js = super(AutoHeavySelect2Mixin, self).render_inner_js_code(id_, *args)
|
||||
js += u"$('#%s').data('field_id', '%s');" % (id_, self.field_id)
|
||||
return js
|
||||
|
||||
class AutoHeavySelect2Widget(AutoHeavySelect2Mixin, HeavySelect2Widget):
|
||||
pass
|
||||
|
|
|
|||
BIN
testapp/test.db
BIN
testapp/test.db
Binary file not shown.
|
|
@ -1,9 +1,14 @@
|
|||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{form}}
|
||||
</table>
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</form>
|
||||
</form>
|
||||
</body>
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
{% load url from future %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Manual Tests</h1>
|
||||
<ul>
|
||||
<li><a href="{% url 'test_single_value_model_field' %}">Test single selection model fields</a></li>
|
||||
<li><a href="{% url 'test_multi_values_model_field' %}">Test multi selection model fields</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
</body>
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
{% load url from future %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{title}}</h2>
|
||||
<ul>
|
||||
{% for e in object_list %}
|
||||
<li><a href="{% url href e.id %}">{{ e }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ class EmployeeForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Employee
|
||||
|
||||
#class DeptForm(forms.ModelForm):
|
||||
# allotted_rooms = ModelMultipleSelect2Field(queryset=ClassRoom.objects)
|
||||
class DeptForm(forms.ModelForm):
|
||||
allotted_rooms = ModelMultipleSelect2Field(queryset=ClassRoom.objects)
|
||||
|
||||
# class Meta:
|
||||
# model = Dept
|
||||
class Meta:
|
||||
model = Dept
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from django.http import HttpResponseRedirect
|
|||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
|
||||
from .forms import EmployeeForm #, DeptForm
|
||||
from .forms import EmployeeForm, DeptForm
|
||||
from .models import Employee, Dept
|
||||
|
||||
def test_single_value_model_field(request):
|
||||
|
|
|
|||
Loading…
Reference in a new issue