mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-04-12 17:40:58 +00:00
Issue 54. Widget performance fix.
This commit is contained in:
parent
096162ac84
commit
cc94db7e2b
9 changed files with 108 additions and 9 deletions
|
|
@ -116,7 +116,7 @@ try:
|
|||
from .views import Select2View, NO_ERR_RESP
|
||||
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("Django found and fields and widgest loaded.")
|
||||
logger.debug("Django found and fields and widgets loaded.")
|
||||
except ImportError:
|
||||
if logger.isEnabledFor(logging.INFO):
|
||||
logger.info("Django not found.")
|
||||
|
|
|
|||
|
|
@ -318,10 +318,30 @@ class ChoiceMixin(object):
|
|||
result._choices = copy.deepcopy(self._choices, memo)
|
||||
return result
|
||||
|
||||
class FilterableModelChoiceIterator(ModelChoiceIterator):
|
||||
"""
|
||||
Extends ModelChoiceIterator to add the capability to apply additional
|
||||
filter on the passed queryset.
|
||||
"""
|
||||
|
||||
def set_extra_filter(self, **filter_map):
|
||||
"""
|
||||
Applies additional filter on the queryset. This can be called multiple times.
|
||||
|
||||
:param kwargs: The ``**kwargs`` to pass to :py:meth:`django.db.models.query.QuerySet.filter`.
|
||||
If this is not set then additional filter (if) applied before is removed.
|
||||
"""
|
||||
if not hasattr(self, '_original_queryset'):
|
||||
import copy
|
||||
self._original_queryset = copy.deepcopy(self.queryset)
|
||||
if filter_map:
|
||||
self.queryset = self._original_queryset.filter(**filter_map)
|
||||
else:
|
||||
self.queryset = self._original_queryset
|
||||
|
||||
class QuerysetChoiceMixin(ChoiceMixin):
|
||||
"""
|
||||
Overrides ``choices``' getter to return instance of :py:class:`.ModelChoiceIterator`
|
||||
Overrides ``choices``' getter to return instance of :py:class:`.FilterableModelChoiceIterator`
|
||||
instead.
|
||||
"""
|
||||
|
||||
|
|
@ -338,12 +358,17 @@ class QuerysetChoiceMixin(ChoiceMixin):
|
|||
# accessed) so that we can ensure the QuerySet has not been consumed. This
|
||||
# construct might look complicated but it allows for lazy evaluation of
|
||||
# the queryset.
|
||||
return ModelChoiceIterator(self)
|
||||
return FilterableModelChoiceIterator(self)
|
||||
|
||||
choices = property(_get_choices, ChoiceMixin._set_choices)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
result = super(QuerysetChoiceMixin, self).__deepcopy__(memo)
|
||||
# Need to force a new ModelChoiceIterator to be created, bug #11183
|
||||
result.queryset = result.queryset
|
||||
return result
|
||||
|
||||
class ModelChoiceFieldMixin(object):
|
||||
class ModelChoiceFieldMixin(QuerysetChoiceMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
queryset = kwargs.pop('queryset', None)
|
||||
|
|
@ -370,6 +395,8 @@ class ModelChoiceFieldMixin(object):
|
|||
if hasattr(self, '_queryset'):
|
||||
return self._queryset
|
||||
|
||||
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. ###
|
||||
|
||||
|
|
@ -453,6 +480,9 @@ class HeavySelect2FieldBaseMixin(object):
|
|||
if hasattr(self, 'field_id'):
|
||||
self.widget.field_id = self.field_id
|
||||
|
||||
# Widget should have been instantiated by now.
|
||||
self.widget.field = self
|
||||
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
t2 = util.timer_start('HeavySelect2FieldBaseMixin.__init__:choices initialization')
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ Contains all the Django widgets for Select2.
|
|||
|
||||
import logging
|
||||
from itertools import chain
|
||||
import util
|
||||
|
||||
from django import forms
|
||||
from django.utils.encoding import force_unicode
|
||||
|
|
@ -202,6 +203,9 @@ 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)
|
||||
|
|
@ -214,6 +218,7 @@ class Select2Mixin(object):
|
|||
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)
|
||||
|
|
@ -411,9 +416,16 @@ class HeavySelect2Mixin(Select2Mixin):
|
|||
txts = []
|
||||
all_choices = choices if choices else []
|
||||
choices_dict = dict()
|
||||
for val, txt in chain(self.choices, all_choices):
|
||||
self_choices = self.choices
|
||||
|
||||
import fields
|
||||
if isinstance(self_choices, fields.FilterableModelChoiceIterator):
|
||||
self_choices.set_extra_filter(**{'%s__in' % self.field.get_pk_field_name(): selected_choices})
|
||||
|
||||
for val, txt in chain(self_choices, all_choices):
|
||||
val = force_unicode(val)
|
||||
choices_dict[val] = txt
|
||||
|
||||
for val in selected_choices:
|
||||
try:
|
||||
txts.append(choices_dict[val])
|
||||
|
|
|
|||
BIN
testapp/test.db
BIN
testapp/test.db
Binary file not shown.
|
|
@ -11,5 +11,7 @@
|
|||
<li><a href="{% url 'test_mixed_form' %}">Test mixed form. All fields' search must return their own results, not other fields'.</a></li>
|
||||
<li><a href="{% url 'test_init_values' %}">Test that initial values are honored in unbound form</a></li>
|
||||
<li><a href="{% url 'test_list_questions' %}">Test tagging support</a></li>
|
||||
<li><a href="{% url 'test_auto_multivalue_field' %}">Test multi value auto model field.</a></li>
|
||||
<li><a href="{% url 'test_auto_heavy_perf' %}">Test performance. Issue#54.</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from django import forms
|
|||
|
||||
from django_select2 import *
|
||||
|
||||
from .models import Employee, Dept, ClassRoom, Lab, Word, School, Tag, Question
|
||||
from .models import Employee, Dept, ClassRoom, Lab, Word, School, Tag, Question, WordList
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
|
@ -27,6 +27,10 @@ class WordChoices(AutoModelSelect2Field):
|
|||
queryset = Word.objects
|
||||
search_fields = ['word__icontains', ]
|
||||
|
||||
class MultiWordChoices(AutoModelSelect2MultipleField):
|
||||
queryset = Word.objects
|
||||
search_fields = ['word__icontains', ]
|
||||
|
||||
class TagField(AutoModelSelect2TagField):
|
||||
queryset = Tag.objects
|
||||
search_fields = ['tag__icontains', ]
|
||||
|
|
@ -87,7 +91,6 @@ class SelfMultiChoices(AutoSelect2MultipleField):
|
|||
########### Forms ##############]
|
||||
|
||||
class SchoolForm(forms.ModelForm):
|
||||
|
||||
classes = ClassRoomChoices()
|
||||
|
||||
class Meta:
|
||||
|
|
@ -161,3 +164,10 @@ class QuestionForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Question
|
||||
|
||||
class WordsForm(forms.ModelForm):
|
||||
word = WordChoices()
|
||||
words = MultiWordChoices()
|
||||
|
||||
class Meta:
|
||||
model = WordList
|
||||
exclude = ['kind']
|
||||
|
|
|
|||
|
|
@ -52,3 +52,15 @@ class Question(models.Model):
|
|||
|
||||
def __unicode__(self):
|
||||
return unicode(self.question)
|
||||
|
||||
class KeyValueMap(models.Model):
|
||||
key = models.CharField(max_length=200)
|
||||
value = models.CharField(max_length=300)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s=>%s' % (self.key, self.value)
|
||||
|
||||
class WordList(models.Model):
|
||||
kind = models.CharField(max_length=100)
|
||||
word = models.ForeignKey(Word, null=True, blank=True, related_name='wordlist_word')
|
||||
words = models.ManyToManyField(Word, null=True, blank=True, related_name='wordlist_words')
|
||||
|
|
|
|||
|
|
@ -14,4 +14,8 @@ urlpatterns = patterns('testapp.testmain.views',
|
|||
url(r'question/$', 'test_list_questions', name='test_list_questions'),
|
||||
url(r'question/form/([0-9]+)/$', 'test_tagging', name='test_tagging'),
|
||||
url(r'question/form/$', 'test_tagging_new', name='test_tagging_new'),
|
||||
|
||||
url(r'auto_model/form/$', 'test_auto_multivalue_field', name='test_auto_multivalue_field'),
|
||||
|
||||
url(r'auto_heavy/perf_test/$', 'test_auto_heavy_perf', name='test_auto_heavy_perf'),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from django.core.urlresolvers import reverse
|
|||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from .forms import EmployeeForm, DeptForm, MixedForm, InitialValueForm, QuestionForm
|
||||
from .models import Employee, Dept, Question
|
||||
from .forms import EmployeeForm, DeptForm, MixedForm, InitialValueForm, QuestionForm, WordsForm, SchoolForm
|
||||
from .models import Employee, Dept, Question, WordList, School
|
||||
|
||||
def test_single_value_model_field(request):
|
||||
return render(request, 'list.html', {
|
||||
|
|
@ -80,3 +80,32 @@ def test_tagging(request, id):
|
|||
form = QuestionForm(instance=question)
|
||||
return render(request, 'form.html', {'form': form})
|
||||
|
||||
def test_auto_multivalue_field(request):
|
||||
try:
|
||||
s = School.objects.get(id=1)
|
||||
except School.DoesNotExist:
|
||||
s = School(id=1)
|
||||
|
||||
if request.POST:
|
||||
form = SchoolForm(data=request.POST, instance=s)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return HttpResponseRedirect(reverse('home'))
|
||||
else:
|
||||
form = SchoolForm(instance=s)
|
||||
return render(request, 'form.html', {'form': form})
|
||||
|
||||
def test_auto_heavy_perf(request):
|
||||
try:
|
||||
word = WordList.objects.get(kind='Word_Of_Day')
|
||||
except WordList.DoesNotExist:
|
||||
word = WordList(kind='Word_Of_Day')
|
||||
|
||||
if request.POST:
|
||||
form = WordsForm(data=request.POST, instance=word)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return HttpResponseRedirect(reverse('home'))
|
||||
else:
|
||||
form = WordsForm(instance=word)
|
||||
return render(request, 'form.html', {'form': form})
|
||||
|
|
|
|||
Loading…
Reference in a new issue