@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from itertools import chain
from collections import namedtuple
from datetime import datetime
try :
from collections import ChainMap
@ -13,8 +14,10 @@ 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 , ForeignKey , ManyToManyField
from django . db . models . sql . where import ExtraWhere , AND , OR
from django . db import connection
from django . core . exceptions import ValidationError
from haystack . query import SearchQuerySet , RelatedSearchQuerySet , SQ
from functions . model_utils import EnumChoices
@ -47,20 +50,27 @@ class FilterCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
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 )
output . append ( format_html ( u ' <li> {1} <label {0} > {2} </label></li> ' ,
option_label = mark_safe ( force_text ( option_label ) )
output . append ( format_html ( u ' <li> {1} <label {0} ><span> {2} </span> </label></li>' ,
label_for , rendered_cb , option_label ) )
output . append ( u ' </ul> ' )
return mark_safe ( ' \n ' . join ( output ) )
class CountModelMultipleChoiceField ( forms . ModelMultipleChoiceField ) :
class WidgetDefaultMixin ( object ) :
def widget_attrs ( self , widget ) :
attrs = super ( WidgetDefaultMixin , self ) . widget_attrs ( widget )
attrs [ ' class ' ] = ' default '
return attrs
class CountModelMultipleChoiceField ( WidgetDefaultMixin , forms . ModelMultipleChoiceField ) :
# widget = forms.CheckboxSelectMultiple
widget = FilterCheckboxSelectMultiple
def label_from_instance ( self , obj ) :
if obj . get ( ' count ' , None ) is None :
return smart_text ( obj . get ( ' name ' ) )
return u ' {label} ( {count} ) ' . format ( label = smart_text ( obj . get ( ' name ' ) ) , count = obj . get ( ' count ' ) )
return u ' {label} <i> ( {count} )</i> ' . format ( label = smart_text ( obj . get ( ' name ' ) ) , count = obj . get ( ' count ' ) )
def prepare_value ( self , value ) :
if isinstance ( value , dict ) :
@ -90,13 +100,9 @@ class CountModelMultipleChoiceField(forms.ModelMultipleChoiceField):
self . run_validators ( value )
return pks
def widget_attrs ( self , widget ) :
"""
Given a Widget instance ( * not * a Widget class ) , returns a dictionary of
any HTML attributes that should be added to the Widget , based on this
Field .
"""
return { ' class ' : ' default ' }
class FilterTypedMultipleChoiceField ( WidgetDefaultMixin , forms . TypedMultipleChoiceField ) :
pass
fields_mapping = {
@ -121,7 +127,7 @@ class FilterForm(forms.Form):
# MEMBERS = MEMBERS
# VISITORS = VISITORS
# PRICE = PRICE
model = forms . TypedMultipleChoiceField (
model = Filter TypedMultipleChoiceField(
label = _ ( u ' Тип события ' ) , coerce = int ,
choices = TYPES , required = False , widget = FilterCheckboxSelectMultiple ( ) )
theme = CountModelMultipleChoiceField (
@ -136,16 +142,16 @@ class FilterForm(forms.Form):
city = CountModelMultipleChoiceField (
label = _ ( u ' Города ' ) , required = False ,
queryset = City . objects . language ( ) . values ( ' pk ' , ' name ' ) )
price = forms . TypedMultipleChoiceField (
price = Filter TypedMultipleChoiceField(
label = _ ( u ' Стоимость ' ) , coerce = int ,
choices = PRICE ,
required = False , widget = FilterCheckboxSelectMultiple ( ) ,
help_text = _ ( u ' За 1 м<sub>2</sub> необорудованной площади ' ) )
members = forms . TypedMultipleChoiceField (
members = Filter TypedMultipleChoiceField(
label = _ ( u ' Участники ' ) , coerce = int ,
choices = MEMBERS ,
required = False , widget = FilterCheckboxSelectMultiple ( ) )
visitors = forms . TypedMultipleChoiceField (
visitors = Filter TypedMultipleChoiceField(
label = _ ( u ' Посетители ' ) , coerce = int ,
choices = VISITORS ,
required = False , widget = FilterCheckboxSelectMultiple ( ) )
@ -171,6 +177,7 @@ class FilterForm(forms.Form):
# нам нужно сбрасывать сохраненный выбор моделей,
# т.к. после валидации нужно вернуть только выбранные
self . _models = None
self . cleaned_data = getattr ( self , ' cleaned_data ' , { } )
return self . _is_valid
@property
@ -213,16 +220,6 @@ class FilterForm(forms.Form):
self . _lookup_kwargs [ ' price_choice__in ' ] = d . get ( ' price ' )
return self . _lookup_kwargs
# @property
# def lookup_args(self):
# if self._lookup_args is None:
# d = self.cleaned_data
# self._lookup_args = {}
# self._local_fields = []
# if d.get('members'):
# self._local_fields.append('city')
# return self._lookup_args
def filter ( self , qs = None ) :
qs = qs or self . default_filter ( )
# lookup_kwargs = dict(ChainMap({}, *(lookup_kwargs or self.lookup_kwargs).values()))
@ -234,38 +231,43 @@ class FilterForm(forms.Form):
qs = qs . load_all ( )
for model in self . models :
qs = qs . load_all_queryset ( model , model . enable . all ( ) )
qs = qs . filter ( data_end__gte = datetime . now ( ) )
return qs
def recalculate_choices ( self ) :
print ( self . _is_valid )
if self . _is_valid and self . lookup_kwargs :
for field in [ ' theme ' , ' tag ' , ' city ' , ' country ' ] :
# field_qs = self.default_filter(load_all=False)
# if not field_lookup_kwargs:
# continue
# field_qs = (x.id.split('.')[1:] for x in self.filter(qs=field_qs, lookup_kwargs=field_lookup_kwargs) if x.id)
# if field == 'theme':
# self.fields[field].queryset = self.get_theme_choices(field_qs)
values = [ ' pk ' , ' name ' ]
order_by = [ ]
select = self . make_count_select ( field )
qs = self . fields [ field ] . queryset
for key in select . iterkeys ( ) :
values . append ( key )
if key == ' selected ' :
order_by . insert ( 0 , ' - ' + key )
else :
order_by . append ( ' - ' + key )
qs = qs . extra ( select = select )
qs = qs . values ( * values ) . order_by ( * order_by )
# if 'count' in values:
# qs = qs.exclude(count=0)
self . fields [ field ] . queryset = qs
print ( self . fields [ field ] . queryset . query )
for field in [ ' members ' , ' visitors ' , ' price ' ] :
self . fields [ field ] . choices = self . make_local_field_count ( field ) or self . fields [ field ] . choices
# if self._is_valid and self.lookup_kwargs:
for field in [ ' theme ' , ' tag ' , ' city ' , ' country ' ] :
# field_qs = self.default_filter(load_all=False)
# if not field_lookup_kwargs:
# continue
# field_qs = (x.id.split('.')[1:] for x in self.filter(qs=field_qs, lookup_kwargs=field_lookup_kwargs) if x.id)
# if field == 'theme':
# self.fields[field].queryset = self.get_theme_choices(field_qs)
values = [ ' pk ' , ' name ' ]
order_by = [ ]
select = self . make_count_select ( field )
qs = self . fields [ field ] . queryset
for key in select . iterkeys ( ) :
values . append ( key )
if key == ' selected ' :
order_by . insert ( 0 , ' - ' + key )
else :
order_by . append ( ' - ' + key )
qs = qs . extra ( select = select )
if ' count ' in values :
having = [ ''' `count` > 0 ''' ]
if ' selected ' in values :
having . append ( ''' `selected` = 1 ''' )
qs . query . having . add ( ExtraWhere ( having , [ ] ) , OR )
qs = qs . values ( * values ) . order_by ( * order_by )
self . fields [ field ] . queryset = qs
print ( self . fields [ field ] . queryset . query )
for field in [ ' members ' , ' visitors ' , ' price ' ] :
self . fields [ field ] . choices = self . make_local_field_count ( field ) or self . fields [ field ] . choices
# for field in self.fields:
# field = self.fields[field]
@ -326,7 +328,6 @@ class FilterForm(forms.Form):
return joins , where
def make_local_field_count ( self , field ) :
# if 'members' not in self.lookup_kwargs:
sql = ' '
selects = [ ]
l_field = fields_mapping . get ( field , field )
@ -366,6 +367,7 @@ class FilterForm(forms.Form):
selects . append ( select + ' ' . join ( joins ) + ' where ' + ' and ' . join ( where ) + group_by )
sql = ' union ' . join ( selects )
print ( sql )
choices = [ ]
if sql :
with connection . cursor ( ) as c :
@ -374,18 +376,15 @@ class FilterForm(forms.Form):
data = [ mapper ( * raw ) for raw in c . fetchall ( ) ]
for key , val in _values_mapping :
count = sum ( [ getattr ( x , key , 0 ) or 0 for x in data ] )
choices . append ( ( val . get ( ' value ' ) , val . get ( ' label ' ) + ' ( {count} ) ' . format ( count = count ) ) )
choices . append ( ( val . get ( ' value ' ) , val . get ( ' label ' ) + ' <i> ( {count} )</i> ' . format ( count = count ) ) )
return choices
# cursor.execute()
#""" SELECT sum(case when (`exposition_exposition`.`members` < 200) then 1 else 0 end) as 'N200', sum(case when (`exposition_exposition`.`members` >= 200 AND `exposition_exposition`.`members` <= 500) then 2 else 0 end) as 'N200500', sum(case when (`exposition_exposition`.`members` >= 500 AND `exposition_exposition`.`members` <= 1000) then 3 else 0 end) as 'N5001000', sum(case when (`exposition_exposition`.`members` >= 1000 AND `exposition_exposition`.`members` <= 2000) then 4 else 0 end) as 'N10002000', sum(case when (`exposition_exposition`.`members` >= 2000) then 5 else 0 end) as 'N2000'FROM `exposition_exposition_translation` INNER JOIN `exposition_exposition` ON (`exposition_exposition_translation`.`master_id` = `exposition_exposition`.`id`) WHERE `exposition_exposition_translation`.`language_code` = 'ru'union SELECT sum(case when (`conference_conference`.`members` < 200) then 1 else 0 end) as 'N200', sum(case when (`conference_conference`.`members` >= 200 AND `conference_conference`.`members` <= 500) then 1 else 0 end) as 'N200500', sum(case when (`conference_conference`.`members` >= 500 AND `conference_conference`.`members` <= 1000) then 1 else 0 end) as 'N5001000', sum(case when (`conference_conference`.`members` >= 1000 AND `conference_conference`.`members` <= 2000) then 1 else 0 end) as 'N10002000', sum(case when (`conference_conference`.`members` >= 2000) then 1 else 0 end) as 'N2000'FROM `conference_conference_translation` INNER JOIN `conference_conference` ON (`conference_conference_translation`.`master_id` = `conference_conference`.`id`) WHERE `conference_conference_translation`.`language_code` = 'ru' """
def make_default_where ( self , * * kwargs ) :
return ''' ` {db_table} `.`is_published` = True ''' . format ( * * kwargs )
return ''' (` {db_table} `.`is_published` = True) AND (` {db_table} `.`data_end` >= ' {date_today} ' ) ''' \
. format ( date_today = datetime . now ( ) . strftime ( ' % Y- % m- %d ' ) , * * kwargs )
def make_count_select ( self , field ) :
count_ selects = [ ]
selects = [ ]
case = None
count = None
print ( ' looking {} {} ' . format ( field , self . lookup_kwargs ) )
@ -447,12 +446,12 @@ class FilterForm(forms.Form):
where . extend ( _where )
where . append ( self . make_default_where ( db_table = model . _meta . db_table ) )
count_ selects. append ( select + ' ' . join ( joins ) + ' where ' + ' and ' . join ( where ) + group_by )
selects . append ( select + ' ' . join ( joins ) + ' where ' + ' and ' . join ( where ) + group_by )
if len ( count_ selects) == 2 :
count = ' ( {} ) + ( {} ) ' . format ( * count_ selects)
elif len ( count_ selects) == 1 :
count = count_ selects[ 0 ]
if len ( selects ) == 2 :
count = ''' IFNULL( ({0 } ), 0) + IFNULL( ( {1 } ), 0) '' '. format ( * selects )
elif len ( selects ) == 1 :
count = selects [ 0 ]
d = { }
if case is not None :
d [ ' selected ' ] = case