You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
7.9 KiB
182 lines
7.9 KiB
# -*- coding: utf-8 -*-
|
|
|
|
from itertools import chain
|
|
|
|
from django import forms
|
|
from django.utils.translation import get_language, ugettext as _
|
|
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, Q
|
|
|
|
from haystack.query import SearchQuerySet, RelatedSearchQuerySet
|
|
|
|
from functions.model_utils import EnumChoices
|
|
from exposition.models import Exposition
|
|
from conference.models import Conference
|
|
from theme.models import Theme, Tag
|
|
|
|
|
|
class CountModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
|
def label_from_instance(self, obj):
|
|
return u'{label} ({count})'.format(label=smart_text(obj.name), count=obj.count)
|
|
|
|
|
|
|
|
### Делаем выборку по темам, сразу заполняя перевод и кол-во событиый
|
|
## 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)
|
|
extra_theme_expo_count = '''SELECT COUNT(`exposition_exposition_theme`.`exposition_id`) FROM `exposition_exposition_theme` WHERE (`theme_theme`.`id` = `exposition_exposition_theme`.`theme_id`) '''
|
|
extra_theme_conf_count = '''SELECT COUNT(`conference_conference_theme`.`conference_id`) FROM `conference_conference_theme` WHERE (`theme_theme`.`id` = `conference_conference_theme`.`theme_id`) '''
|
|
|
|
extra_tag_expo_count = '''SELECT COUNT(`exposition_exposition_tag`.`exposition_id`) FROM `exposition_exposition_tag` WHERE (`theme_tag`.`id` = `exposition_exposition_tag`.`tag_id`) '''
|
|
extra_tag_conf_count = '''SELECT COUNT(`conference_conference_tag`.`conference_id`) FROM `conference_conference_tag` WHERE (`theme_tag`.`id` = `conference_conference_tag`.`tag_id`) '''
|
|
|
|
class FilterForm(forms.Form):
|
|
TYPES = EnumChoices(
|
|
EXPO=(1, _(u'Выставки')),
|
|
CONF=(2, _(_(u'Конференции'))),
|
|
)
|
|
|
|
model = forms.TypedMultipleChoiceField(label=_(u'Тип события'), coerce=int, choices=TYPES, required=False, widget=forms.CheckboxSelectMultiple())
|
|
theme = CountModelMultipleChoiceField(label=_(u'Тематики'),
|
|
queryset=Theme.objects.none(),
|
|
required=False, widget=forms.CheckboxSelectMultiple())
|
|
tag = CountModelMultipleChoiceField(label=_(u'Теги'),
|
|
queryset=Tag.objects.none(),
|
|
required=False, widget=forms.CheckboxSelectMultiple())
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(FilterForm, self).__init__(*args, **kwargs)
|
|
self._is_valid = False
|
|
self._models = None
|
|
self.fields['theme'].queryset = self.get_theme_choices()
|
|
self.fields['tag'].queryset = self.get_tag_choices()
|
|
|
|
def is_valid(self):
|
|
# if getattr(self, '_is_valid', None) is None:
|
|
self._is_valid = super(FilterForm, self).is_valid()
|
|
|
|
# нам нужно сбрасывать сохраненные модели,
|
|
# т.к. после валидации нужно вернуть только выбранные
|
|
self._models = None
|
|
return self._is_valid
|
|
|
|
@property
|
|
def models(self):
|
|
if self._models is None and self._is_valid:
|
|
val = self.cleaned_data.get('model')
|
|
self._models = []
|
|
if self.TYPES.EXPO in val:
|
|
self._models.append(Exposition)
|
|
if self.TYPES.CONF in val:
|
|
self._models.append(Conference)
|
|
return self._models or [Exposition, Conference]
|
|
|
|
def get_theme_choices(self):
|
|
# 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
|
|
|
|
if getattr(self, '_theme_choices', None) is None:
|
|
if Exposition in self.models and Conference in self.models:
|
|
count_query = '({q1}) + ({q2})'.format(
|
|
q1=extra_theme_expo_count,
|
|
q2=extra_theme_conf_count)
|
|
filter_types = Q(types=Theme.types.conference) | Q(types=Theme.types.exposition)
|
|
elif Exposition in self.models:
|
|
count_query = extra_theme_expo_count
|
|
filter_types = Q(types=Theme.types.exposition)
|
|
elif Conference in self.models:
|
|
count_query = extra_theme_conf_count
|
|
filter_types = Q(types=Theme.types.conference)
|
|
|
|
self._theme_choices = Theme.objects.language()\
|
|
.filter(filter_types)\
|
|
.extra(select={'count': count_query})\
|
|
.order_by('-count', '-name')
|
|
return self._theme_choices
|
|
|
|
def get_tag_choices(self):
|
|
extra_tag_select = {}
|
|
if getattr(self, '_tag_choices', None) is None:
|
|
if Exposition in self.models and Conference in self.models:
|
|
count_query = '({q1}) + ({q2})'.format(
|
|
q1=extra_tag_expo_count,
|
|
q2=extra_tag_conf_count)
|
|
elif Exposition in self.models:
|
|
count_query = extra_tag_expo_count
|
|
elif Conference in self.models:
|
|
count_query = extra_tag_conf_count
|
|
self._tag_choices = Tag.objects.language()\
|
|
.extra(select={'count': count_query})\
|
|
.order_by('-count', '-name')
|
|
return self._tag_choices
|
|
|
|
def filter(self):
|
|
qs = self.default_filter()
|
|
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):
|
|
qs = RelatedSearchQuerySet().models(*self.models).load_all()
|
|
for model in self.models:
|
|
qs = qs.load_all_queryset(model,
|
|
model.enable.all()
|
|
# не реализовано в hvad <_<
|
|
# .only(
|
|
# 'canceled', 'name', 'main_title', 'expohit', 'logo',
|
|
# 'quality_label', 'services', 'visitors', 'members',
|
|
# 'data_begin', 'data_end', 'country__url', 'country__name',
|
|
# 'city__url', 'place__name'
|
|
# )
|
|
)
|
|
return qs
|
|
|