Django

Code

AJAXForeignKey: forms.py

File forms.py, 5.0 kB (added by Michael Axiak <axiak@mit.edu>, 1 year ago)

form field definition

Line 
1 from django import oldforms
2 from django.template.defaultfilters import addslashes
3 import re
4
5 get_id_re = re.compile('\D*(\d+)\D$')
6
7 class AjaxForeignKeyFormField(oldforms.FormField):
8     def __init__(self, field_name, field,
9                  is_required=False, validator_list=None,
10                  member_name=None, ajax_func = None, width=None,
11                  help_text = None):
12
13         self.field_name  = field_name
14         self.field       = field
15         self.is_required = is_required
16         self.help_text   = help_text
17
18         if width is None:
19             self.width = '25em'
20         else:
21             self.width = width
22
23         if ajax_func is None:
24             self.ajax_func = 'ajax_autocomplete'
25         else:
26             self.ajax_func = ajax_func
27        
28         if validator_list is None:
29             validator_list = []
30
31         self.validator_list = validator_list
32
33     def extract_data(self, data):
34         return data.get(self.field.attname, '')
35
36
37     def render(self, data):
38         """
39         Renders the actual ajax widget.
40         """
41
42         init_val = data
43
44         if data:
45             objects = self.field.rel.to.objects.filter(pk = data)
46             if objects.count() == 1:
47                 obj = objects[0]
48                 if hasattr(obj, 'ajax_str'):
49                     init_val = obj.ajax_str() + " (%s)" % data
50                 else:
51                     init_val = obj.__str__() + " (%s)" % data
52         else:
53             data = init_val = ''
54
55
56         fn = self.field_name
57         related_model = self.field.rel.to
58
59         model_module = related_model.__module__
60         model_name   = related_model.__name__
61        
62         javascript = """
63 <script type="text/javascript">
64 <!--
65
66 document.getElementById("id_%s").value = "%s";
67
68 var %s_data = new YAHOO.widget.DS_XHR("/admin/ajax_autocomplete/",
69                                       ['result','ajax_str','id']);
70
71 %s_data.scriptQueryParam  = "ajax_data";
72 %s_data.scriptQueryAppend = "model_module=%s&model_name=%s&ajax_func=%s";
73 %s_data.connTimeout = 3000;
74
75 var autocomp__%s = new YAHOO.widget.AutoComplete("id_%s","id_%s__container",%s_data);
76
77 autocomp__%s.allowBrowserAutocomplete = false;
78 //autocomp__%s.typeAhead = true;
79 autocomp__%s.animVert = true;
80 autocomp__%s.queryDelay = .2;
81 autocomp__%s.maxCacheEntries = 60;
82 autocomp__%s.animSpeed = .5;
83 autocomp__%s.useShadow = true;
84 autocomp__%s.useIFrame = true;
85
86
87 YAHOO.util.Event.addListener(window, "load", function (e) {
88   var elements = YAHOO.util.Dom.getElementsByClassName('add-another', 'a');
89   for (var i=0; i<elements.length; i++) {
90     elements[i].style.display = 'none';
91   }
92   var elements = YAHOO.util.Dom.getElementsByClassName('form-row', 'div');
93   for (var i=0; i< elements.length; i++) {
94     elements[i].style.overflow = 'visible';
95     if (YAHOO.util.Dom.getElementsByClassName('yui_autocomplete','div', elements[i]).length > 0) {
96         elements[i].style.paddingBottom = '12.5em';
97     }
98   }
99 });
100
101 //-->
102 </script>""" % \
103          (fn, addslashes(init_val),
104           fn, fn, fn, model_module, model_name, self.ajax_func,
105           fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn)
106
107         css = """
108 <style type="text/css">
109     /* Taken from Yahoo... */
110     #id_%s__yui_autocomplete {position:relative;width:%s;margin-bottom:1em;}/* set width of widget here*/
111     #id_%s__yui_autocomplete {z-index:0} /* for IE z-index of absolute divs inside relative divs issue */
112     #id_%s__yui_autocomplete input {_position:absolute;width:100%%;height:1.4em;z-index:0;} /* abs for ie quirks */
113     #id_%s__container {position:relative; width:100%%;}
114     #id_%s__container .yui-ac-content {position:absolute;width:100%%;border:1px solid #ccc;background:#fff;overflow:hidden;z-index:9050;}
115     #id_%s__container .yui-ac-shadow {position:absolute;margin:.3em;width:100%%;background:#eee;z-index:8000;}
116     #id_%s__container ul {padding:5px 0;width:100%%; list-item-type: none;margin-left: 0; padding-left: 0;z-index:9000;}
117     #id_%s__container li {padding:0 5px;cursor:default;white-space:nowrap;padding-left: 0; margin-left: 0;}
118     #id_%s__container li.yui-ac-highlight {background:#9cf;z-index:9000;}
119     #id_%s__container li.yui-ac-prehighlight {background:#CCFFFF;z-index:9000;}
120     .yui-ac-bd { padding:0; margin: 0; z-index:9000;}
121 </style>
122 """ % \
123         (fn,self.width,fn,fn,fn,fn,fn,fn,fn,fn,fn)
124
125         html = """
126 <div class="container" style="position: relative;">
127 <div class="yui_autocomplete" id="id_%s__yui_autocomplete">
128   <input type="text" id="id_%s" name="%s" class="vCharField" value="%s" />
129   <div id="id_%s__container" class="yui_container"></div>
130 </div>
131 </div>
132
133 """ % (fn,fn,fn,addslashes(init_val),fn)
134
135         return css + html + javascript
136
137     def prepare(self, new_data):
138         id = new_data.get(self.field_name, None)
139         if id:
140             try:
141                 id = int(id)
142             except ValueError:
143                 match = get_id_re.match(id)
144                 id = match.groups()[0]
145         new_data[self.field_name] = id