diff --git a/django_select2/forms.py b/django_select2/forms.py index 81d87e0..da6af18 100644 --- a/django_select2/forms.py +++ b/django_select2/forms.py @@ -300,7 +300,25 @@ class ModelSelect2Mixin(object): defaults.update(kwargs) super(ModelSelect2Mixin, self).__init__(*args, **defaults) - def filter_queryset(self, term): + def set_to_cache(self): + """ + Add widget's attributes to Djnago's cache. + + Split the queryset, to not pickle the result set. + """ + queryset = self.get_queryset() + cache.set(self._get_cache_key(), { + 'queryset': + [ + queryset.none(), + queryset.query, + ], + 'cls': self.__class__, + 'search_fields': self.search_fields, + 'max_results': self.max_results, + }) + + def filter_queryset(self, term, queryset=None): """ Return queryset filtered by search_fields matching the passed term. @@ -309,7 +327,8 @@ class ModelSelect2Mixin(object): :return: Filtered queryset :rtype: :class:`.django.db.models.QuerySet` """ - qs = self.get_queryset() + if not queryset: + queryset = self.get_queryset() search_fields = self.get_search_fields() select = Q() term = term.replace('\t', ' ') @@ -317,7 +336,7 @@ class ModelSelect2Mixin(object): for t in [t for t in term.split(' ') if not t == '']: select &= reduce(lambda x, y: x | Q(**{y: t}), search_fields, Q(**{search_fields.pop(): t})) - return qs.filter(select).distinct() + return queryset.filter(select).distinct() def get_queryset(self): """ diff --git a/django_select2/views.py b/django_select2/views.py index c1adab2..f410ffd 100644 --- a/django_select2/views.py +++ b/django_select2/views.py @@ -52,7 +52,7 @@ class AutoResponseView(BaseListView): def get_queryset(self): """Get queryset from cached widget.""" - return self.widget.filter_queryset(self.term) + return self.widget.filter_queryset(self.term, self.queryset) def get_paginate_by(self, queryset): """Paginate response by size of widget's `max_results` parameter.""" @@ -76,7 +76,11 @@ class AutoResponseView(BaseListView): raise Http404('Invalid "field_id".') else: cache_key = '%s%s' % (settings.SELECT2_CACHE_PREFIX, key) - widget = cache.get(cache_key) - if widget is None: + widget_dict = cache.get(cache_key) + if widget_dict is None: raise Http404('field_id not found') - return widget + qs, qs.query = widget_dict.pop('queryset') + self.queryset = qs.all() + widget_dict['queryset'] = self.queryset + widget_cls = widget_dict.pop('cls') + return widget_cls(**widget_dict) diff --git a/tests/test_forms.py b/tests/test_forms.py index 0054f2b..bdac34f 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -161,13 +161,13 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin): assert result.exists() def test_queryset_kwarg(self): - widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains']) + widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains']) genre = Genre.objects.last() result = widget.filter_queryset(genre.title) assert result.exists() def test_ajax_view_registration(self, client): - widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains']) + widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains']) widget.render('name', 'value') url = reverse('django_select2-json') genre = Genre.objects.last() @@ -178,16 +178,20 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin): assert genre.pk in [result['id'] for result in data['results']] def test_render(self): - widget = ModelSelect2Widget() + widget = ModelSelect2Widget(queryset=Genre.objects.all()) widget.render('name', 'value') cached_widget = cache.get(widget._get_cache_key()) - assert isinstance(cached_widget, ModelSelect2Widget) + assert cached_widget['max_results'] == widget.max_results + assert cached_widget['search_fields'] == widget.search_fields + qs = widget.get_queryset() + assert isinstance(cached_widget['queryset'][0], qs.__class__) + assert text_type(cached_widget['queryset'][1]) == text_type(qs.query) class TestHeavySelect2TagWidget(TestHeavySelect2Mixin): def test_tag_attrs(self): - widget = ModelSelect2TagWidget(queryset=Genre.objects, search_fields=['title__icontains']) + widget = ModelSelect2TagWidget(queryset=Genre.objects.all(), search_fields=['title__icontains']) output = widget.render('name', 'value') assert 'data-minimum-input-length="1"' in output assert 'data-tags="true"' in output