сделан подсчет кол-ва резальтатов для пунктов в фильтре, проведена оптимизация запросов к бд (150мс -> 50мс, 184 запроса -> 56)

remotes/origin/stage5
Alexander Burdeiny 10 years ago
parent 51d8e14d58
commit bdd4960d68
  1. 55
      accounts/models.py
  2. 136
      events/forms.py
  3. 2
      events/views.py
  4. 2
      templates/client/includes/events/filter_form.html

@ -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))\

@ -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 = ['<ul>']
# 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'<li><label{0}>{1} {2} ({count})</label></li>',
label_for, rendered_cb, option_label,
count=count))
output.append('</ul>')
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

@ -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):

@ -6,7 +6,7 @@
{% for field in form %}
{% if field.errors %}error{% endif %}
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }} (763)
{{ field }}
{{ field.errors }}
{{ field.help_text }}
{% endfor %}

Loading…
Cancel
Save