Code

Ticket #4467: categorizedmodelchoicefield.py

File categorizedmodelchoicefield.py, 4.8 KB (added by ulda <ulf.dambacher@…>, 7 years ago)
Line 
1# (c) + (w) by ulf.dambacher _ at _ web.de
2# License: select one from GPL Version 2 or the BSD license django uses
3#
4from django.newforms.widgets import Select
5from django.newforms.fields import ChoiceField,Field
6from django.newforms.models import ModelChoiceField
7from django.newforms.util import ValidationError
8from django.utils.translation import gettext
9
10from gertus.lib.JsonResponse import *
11
12
13class CategorizedSelect(Select):
14
15    category_field= '%s_category'
16    value_field = '%s'
17
18    def __init__(self, attrs=None, categories=(),choices=(),callback_link='',settings_for_value=None ):
19        self.categories=list(categories)
20        self.choices=list(choices)
21        self.attrs = attrs or {}
22        self.callback_link=callback_link
23        self.settings_for_value=settings_for_value
24
25    def render(self, name, value, attrs=None):
26
27
28        category_value=None
29        value_value=None
30        if value:
31            value_value=value
32            if self.settings_for_value:
33                (self.categories,self.choices,category_value) = self.settings_for_value(value)
34
35        output = [
36"""
37<script type="text/javascript">
38""",
39'function %s_update(select)  {' % (self.value_field % name),
40"""
41  //loesche Auswahlliste
42""",
43'  var elem= select.form.elements["%s"]'% (self.value_field % name)  ,
44"""       
45  for(i = 0; i<= elem.length; i+=1 )
46        elem.options[i]=null;
47
48  //und lade die Liste neu
49  req = new HTTP.Request({
50    method:'get',
51
52""",
53"    uri: '%s', \n" % self.callback_link,
54"""
55    parameters: 'category='+select.options[select.options.selectedIndex].value,
56    onSuccess: function (trans) {
57      var data;
58      try {
59        var data = eval('('+trans.responseText+')');
60      } catch(e) {
61        alert('eval: Ungueltiges JSON: ' + e);
62        return;
63      };
64
65      // alert(trans.responseText);
66
67      for ( i=0;i < data.length; i+=1) {
68        elem.add(new Option( data[i][1], data[i][0], false , false ) , null );
69      };
70    }
71  });
72}
73</script>
74"""
75        ]
76
77        select_html = Select(choices=self.categories).render(
78            self.category_field % name,
79            category_value,
80            attrs={ 'onchange': "%s_update(this)" % (self.value_field %name) },
81            )
82        output.append(select_html)
83
84        select_html = Select(choices=self.choices).render(
85            self.value_field % name,
86            value_value,
87            )
88        output.append(select_html)
89       
90        return u'\n'.join(output)
91
92    def value_from_datadict(self, data, name):
93        return data.get(self.value_field % name)
94   
95
96class CategorizedModelChoiceField(ModelChoiceField):
97    """A ChoiceField for selecting for selecting from a queryset
98    but with narrowing the selection by using a category"""
99
100    def __init__(self, queryset, category, callback_link, empty_label=u"---------", 
101            required=True, widget=CategorizedSelect, label=None, initial=None, help_text=None):
102
103        # Call Field instead of ChoiceField __init__() because we don't need
104        # ChoiceField.__init__().
105        Field.__init__(self, required, widget, label, initial, help_text)
106        self.queryset = queryset
107        self.category_name=category
108        self.empty_label = empty_label
109        self.widget.categories=self.categories
110        self.widget.choices=self.get_choices_for_category(self.categories[0][0])
111        self.widget.callback_link=self.callback_link=callback_link
112        self.widget.settings_for_value=lambda x: self.widgetsettings_for_value(x)
113
114    def _get_categories(self):
115
116        cat=set()
117        for i in self.queryset.values( self.category_name):
118            cat.add( i[self.category_name])
119
120        choices=dict(self.queryset.model._meta.get_field(self.category_name).choices)
121        return [ ( i , choices.get(i,i)  ) for i in cat]
122       
123    def _set_categories(self,value):
124        self._categories=self.widget.categories=list(value)
125
126    def get_choices_for_category(self, category):
127        my_list=[]
128        for obj in self.queryset.filter( **{ self.category_name: category} ):
129            my_list.append(  (  obj.id, str(obj)  ) )
130        return my_list
131
132    categories= property(_get_categories, _set_categories)
133
134    #callback-function for widget
135    def widgetsettings_for_value(self,value):
136        obj = self.queryset.model._default_manager.get(pk=value)
137        category=getattr(obj, self.category_name)
138        return (self.categories,self.get_choices_for_category(category),category)
139
140
141# this defines a view for the callback-url
142def get_choices_for_category(request, queryset, category_name):
143    my_category=request.GET.get('category',None)
144
145    my_list=[]
146    if my_category:
147        for obj in queryset.filter( **{ category_name: my_category} ):
148            my_list.append(  (  obj.id, str(obj)  ) )
149    else:
150        my_list.append( ('-', 'please select a category first' ) )
151
152    return JsonResponse(my_list)