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 = ['
']
- # Normalize to strings
- str_values = set([force_text(v) for v in value])
- for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
- # If an ID attribute was given, add a numeric index as a suffix,
- # so that the checkboxes don't all have the same ID attribute.
- if has_id:
- final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
- label_for = format_html(u' for="{0}"', final_attrs['id'])
- else:
- label_for = ''
-
- cb = forms.CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
- option_value = force_text(option_value)
- rendered_cb = cb.render(name, option_value)
- option_label = force_text(option_label)
- count = 763
- output.append(format_html(u'',
- label_for, rendered_cb, option_label,
- count=count))
- output.append('
')
- 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 %}