diff --git a/accounts/models.py b/accounts/models.py index a49485c7..438ec2e6 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -230,20 +230,59 @@ class Calendar(models.Model): def get_expos(self): # 1 query - return list(self.expositions.language().all()) + _get_expos = getattr(self, '_get_expos', None) + if _get_expos is None: + self._get_expos = list(self.expositions.language().all()) + return self._get_expos + + def get_expos_ids(self): + # 1 query + _get_expos_ids = getattr(self, '_get_expos_ids', None) + if _get_expos_ids is None: + self._get_expos_ids = list(self.expositions.all().values_list('pk', flat=True)) + return self._get_expos_ids def get_confs(self): # 1 query - return list(self.conferences.language().all()) + _get_confs = getattr(self, '_get_confs', None) + if _get_confs is None: + self._get_confs = list(self.conferences.language().all()) + return self._get_confs + + def get_confs_ids(self): + # 1 query + _get_confs_ids = getattr(self, '_get_confs_ids', None) + if _get_confs_ids is None: + self._get_confs_ids = list(self.conferences.all().values_list('pk', flat=True)) + return self._get_confs_ids def get_seminars(self): # 1 query - return list(self.seminars.language().all()) + _get_seminars = getattr(self, '_get_seminars', None) + if _get_seminars is None: + self._get_seminars = list(self.seminars.language().all()) + return self._get_seminars + + def get_seminars_ids(self): + # 1 query + _get_seminars_ids = getattr(self, '_get_seminars_ids', None) + if _get_seminars_ids is None: + self._get_seminars_ids = list(self.seminars.all().values_list('pk', flat=True)) + return self._get_seminars_ids def get_webinars(self): # 1 query - return list(self.webinars.language().all()) + _get_webinars = getattr(self, '_get_webinars', None) + if _get_webinars is None: + self._get_webinars = list(self.webinars.language().all()) + return self._get_webinars + def get_webinars_ids(self): + # 1 query + _get_webinars_ids = getattr(self, '_get_webinars_ids', None) + if _get_webinars_ids is None: + self._get_webinars_ids = list(self.webinars.all().values_list('pk', flat=True)) + return self._get_webinars_ids def get_events(self): # 4 query @@ -257,13 +296,13 @@ class Calendar(models.Model): """ event_type = event.event_type if event_type == 'expo': - return event in self.get_expos() + return event in self.get_expos_ids() elif event_type == 'conf': - return event in self.get_confs() + return event in self.get_confs_ids() elif event_type == 'seminar': - return event in self.get_seminars() + return event in self.get_seminars_ids() elif event_type == 'webinar': - return event in self.get_webinars() + return event in self.get_webinars_ids() def events_by_month(self, day): exp = list(self.expositions.filter((Q(data_begin__month=day.month) & Q(data_begin__year=day.year))\ diff --git a/events/forms.py b/events/forms.py index b4e56403..013d46e9 100644 --- a/events/forms.py +++ b/events/forms.py @@ -4,12 +4,12 @@ from itertools import chain from django import forms from django.utils.translation import ugettext as _ -from django.utils.encoding import force_text +from django.utils.encoding import smart_text, force_text from django.utils.html import format_html from django.utils.safestring import mark_safe from django.db.models import Count, Sum -from haystack.query import SearchQuerySet +from haystack.query import SearchQuerySet, RelatedSearchQuerySet from functions.model_utils import EnumChoices from exposition.models import Exposition @@ -17,57 +17,94 @@ from conference.models import Conference from theme.models import Theme, Tag -class CheckboxSelectMultiple(forms.CheckboxSelectMultiple): - def render(self, name, value, attrs=None, choices=()): - if value is None: value = [] - has_id = attrs and 'id' in attrs - final_attrs = self.build_attrs(attrs, name=name) - output = ['') - return mark_safe('\n'.join(output)) +class CountModelMultipleChoiceField(forms.ModelMultipleChoiceField): + def label_from_instance(self, obj): + return u'{0} ({count})'.format(smart_text(obj.name), count=obj.e_count + obj.c_count) -class FilterForm(forms.Form): - # class Meta: - # widgets = { - # 'model': forms.CheckboxSelectMultiple(), - # 'theme': forms.CheckboxSelectMultiple(), - # 'tag': forms.CheckboxSelectMultiple(), - # } +### Делаем выборку по темам, сразу заполняя перевод и кол-во событиый +## 1-й рабочий способ (без заполнения перевода) +# """ +# SELECT +# `theme_theme`.`id`, +# ( +# SELECT COUNT(`exposition_exposition_theme`.`exposition_id`) +# FROM `exposition_exposition_theme` +# WHERE (`theme_theme`.`id` = `exposition_exposition_theme`.`theme_id`) +# ) AS `e_count`, +# ( +# SELECT COUNT(`conference_conference_theme`.`conference_id`) +# FROM `conference_conference_theme` +# WHERE (`theme_theme`.`id` = `conference_conference_theme`.`theme_id`) +# ) AS `c_count`, +# `theme_theme_translation`.`name` as `name` +# FROM +# `theme_theme` +# GROUP BY `theme_theme`.`id` +# ORDER BY NULL +# """ + +## 2-й рабочий способ (с заполнением перевода) +## аттрибут перевода 'name' присвоен на 'name_t', чтобы не ругалось на неправильно заполненый перевод +# theme_sql = \ +# """ +# SELECT +# `theme_theme_translation`.`name` AS `name_t`, +# `theme_theme_translation`.`master_id` AS `id`, +# ( +# SELECT COUNT(`exposition_exposition_theme`.`exposition_id`) +# FROM `exposition_exposition_theme` +# WHERE (`theme_theme_translation`.`master_id` = `exposition_exposition_theme`.`theme_id`) +# ) AS `e_count`, +# ( +# SELECT COUNT(`conference_conference_theme`.`conference_id`) +# FROM `conference_conference_theme` +# WHERE (`theme_theme_translation`.`master_id` = `conference_conference_theme`.`theme_id`) +# ) AS `c_count` +# FROM +# `theme_theme_translation` +# WHERE (`theme_theme_translation`.`language_code` = 'ru') +# GROUP BY `theme_theme_translation`.`name` +# ORDER BY NULL +# """ +# qs = Theme.objects.raw(theme_sql) + +## 3-й рабочий способ (с родным заполением перевода) +## в ходе поиска, был найден и 4-й рабочий способ используя RawSQL, но он для Django >= 1.8 +## https://docs.djangoproject.com/en/1.9/ref/models/expressions/#raw-sql-expressions +## from django.db.models.expressions import RawSQL +extra_themes_expo_count = ''' +SELECT COUNT(`exposition_exposition_theme`.`exposition_id`) +FROM `exposition_exposition_theme` +WHERE (`theme_theme`.`id` = `exposition_exposition_theme`.`theme_id`) +''' +extra_themes_conf_count = ''' +SELECT COUNT(`conference_conference_theme`.`conference_id`) +FROM `conference_conference_theme` +WHERE (`theme_theme`.`id` = `conference_conference_theme`.`theme_id`) +''' +extra_themes_select = { + 'e_count': extra_themes_expo_count, + 'c_count': extra_themes_conf_count, +} +themes = Theme.objects.language('ru').extra(select=extra_themes_select) + + +class FilterForm(forms.Form): TYPES = EnumChoices( EXPO=(1, _(u'Выставки')), CONF=(2, _(_(u'Конференции'))), ) - model = forms.TypedMultipleChoiceField(label=_(u'Тип события'), coerce=int, choices=TYPES, required=False, widget=CheckboxSelectMultiple()) - theme = forms.ModelMultipleChoiceField(label=_(u'Тематики'), - queryset=Theme.active.all()\ - .annotate(e_count=Count('exposition_themes'), c_count=Count('conference_themes')), - required=False, widget=CheckboxSelectMultiple()) - tag = forms.ModelMultipleChoiceField(label=_(u'Теги'), - queryset=Tag.active.all()\ - .annotate(e_count=Count('exposition_themes'), c_count=Count('conference_themes')), - required=False, widget=CheckboxSelectMultiple()) + model = forms.TypedMultipleChoiceField(label=_(u'Тип события'), coerce=int, choices=TYPES, required=False, widget=forms.CheckboxSelectMultiple()) + theme = CountModelMultipleChoiceField(label=_(u'Тематики'), + queryset=themes, + required=False, widget=forms.CheckboxSelectMultiple()) + # tag = CountModelMultipleChoiceField(label=_(u'Теги'), + # queryset=Tag.active.all()\ + # .annotate(e_count=Count('exposition_tags'), c_count=Count('conference_tags')), + # required=False, widget=forms.CheckboxSelectMultiple()) def get_models(self): @@ -83,10 +120,17 @@ class FilterForm(forms.Form): return models def filter(self): - qs = SearchQuerySet().models(*self.get_models()).all() + qs = self.default_filter(self.get_models()) d = self.cleaned_data if d.get('theme'): qs = qs.filter(theme__in=d.get('theme')) if d.get('tag'): qs = qs.filter(tag__in=d.get('tag')) return qs + + def default_filter(self, models=None): + qs = RelatedSearchQuerySet().models(Exposition, Conference).load_all() + models = models or [Exposition, Conference] + for model in models: + qs = qs.load_all_queryset(model, model.enable.all()) + return qs diff --git a/events/views.py b/events/views.py index 462b54c6..7daff5ff 100644 --- a/events/views.py +++ b/events/views.py @@ -27,7 +27,7 @@ class FilterListView(ContextMixin, FormMixin, ListView): if self.form.is_valid(): qs = self.form.filter() else: - qs = SearchQuerySet().models(Exposition, Conference).all() + qs = self.form.default_filter() return qs def get(self, request, *args, **kwargs): diff --git a/templates/client/includes/events/filter_form.html b/templates/client/includes/events/filter_form.html index e6cbd559..077f1dad 100644 --- a/templates/client/includes/events/filter_form.html +++ b/templates/client/includes/events/filter_form.html @@ -6,7 +6,7 @@ {% for field in form %} {% if field.errors %}error{% endif %} - {{ field }} (763) + {{ field }} {{ field.errors }} {{ field.help_text }} {% endfor %}