# -*- coding: utf-8 -*- from itertools import chain from django import forms from django.utils.translation import 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 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'{0} ({count})'.format(smart_text(obj.name), count=obj.e_count + obj.c_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) ## 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=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): val = self.cleaned_data.get('model') models = [] if val: if self.TYPES.EXPO in val: models.append(Exposition) if self.TYPES.CONF in val: models.append(Conference) else: models = [Conference, Exposition] return models def filter(self): 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