Django

Code

root/django/branches/gis/django/contrib/admin/widgets.py

Revision 8215, 8.5 kB (checked in by jbronn, 4 months ago)

gis: Merged revisions 7981-8001,8003-8011,8013-8033,8035-8036,8038-8039,8041-8063,8065-8076,8078-8139,8141-8154,8156-8214 via svnmerge from trunk.

Line 
1 """
2 Form Widget classes specific to the Django admin site.
3 """
4
5 import copy
6
7 from django import forms
8 from django.forms.widgets import RadioFieldRenderer
9 from django.forms.util import flatatt
10 from django.utils.text import truncate_words
11 from django.utils.translation import ugettext as _
12 from django.utils.safestring import mark_safe
13 from django.utils.encoding import force_unicode
14 from django.conf import settings
15
16 class FilteredSelectMultiple(forms.SelectMultiple):
17     """
18     A SelectMultiple with a JavaScript filter interface.
19
20     Note that the resulting JavaScript assumes that the SelectFilter2.js
21     library and its dependencies have been loaded in the HTML page.
22     """
23     def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
24         self.verbose_name = verbose_name
25         self.is_stacked = is_stacked
26         super(FilteredSelectMultiple, self).__init__(attrs, choices)
27
28     def render(self, name, value, attrs=None, choices=()):
29         output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
30         output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
31         # TODO: "id_" is hard-coded here. This should instead use the correct
32         # API to determine the ID dynamically.
33         output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
34             (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
35         return mark_safe(u''.join(output))
36
37 class AdminDateWidget(forms.TextInput):
38     class Media:
39         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
40               settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
41        
42     def __init__(self, attrs={}):
43         super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
44
45 class AdminTimeWidget(forms.TextInput):
46     class Media:
47         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
48               settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
49
50     def __init__(self, attrs={}):
51         super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
52    
53 class AdminSplitDateTime(forms.SplitDateTimeWidget):
54     """
55     A SplitDateTime Widget that has some admin-specific styling.
56     """
57     def __init__(self, attrs=None):
58         widgets = [AdminDateWidget, AdminTimeWidget]
59         # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
60         # we want to define widgets.
61         forms.MultiWidget.__init__(self, widgets, attrs)
62
63     def format_output(self, rendered_widgets):
64         return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
65             (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
66
67 class AdminRadioFieldRenderer(RadioFieldRenderer):
68     def render(self):
69         """Outputs a <ul> for this set of radio fields."""
70         return mark_safe(u'<ul%s>\n%s\n</ul>' % (
71             flatatt(self.attrs),
72             u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
73         )
74
75 class AdminRadioSelect(forms.RadioSelect):
76     renderer = AdminRadioFieldRenderer
77
78 class AdminFileWidget(forms.FileInput):
79     """
80     A FileField Widget that shows its current value if it has one.
81     """
82     def __init__(self, attrs={}):
83         super(AdminFileWidget, self).__init__(attrs)
84        
85     def render(self, name, value, attrs=None):
86         output = []
87         if value:
88             output.append('%s <a target="_blank" href="%s%s">%s</a> <br />%s ' % \
89                 (_('Currently:'), settings.MEDIA_URL, value, value, _('Change:')))
90         output.append(super(AdminFileWidget, self).render(name, value, attrs))
91         return mark_safe(u''.join(output))
92
93 class ForeignKeyRawIdWidget(forms.TextInput):
94     """
95     A Widget for displaying ForeignKeys in the "raw_id" interface rather than
96     in a <select> box.
97     """
98     def __init__(self, rel, attrs=None):
99         self.rel = rel
100         super(ForeignKeyRawIdWidget, self).__init__(attrs)
101
102     def render(self, name, value, attrs=None):
103         related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
104         if self.rel.limit_choices_to:
105             url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in self.rel.limit_choices_to.items()])
106         else:
107             url = ''
108         if not attrs.has_key('class'):
109           attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
110         output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
111         # TODO: "id_" is hard-coded here. This should instead use the correct
112         # API to determine the ID dynamically.
113         output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
114             (related_url, url, name))
115         output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % settings.ADMIN_MEDIA_PREFIX)
116         if value:
117             output.append(self.label_for_value(value))
118         return mark_safe(u''.join(output))
119    
120     def label_for_value(self, value):
121         return '&nbsp;<strong>%s</strong>' % \
122             truncate_words(self.rel.to.objects.get(pk=value), 14)
123            
124 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
125     """
126     A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
127     in a <select multiple> box.
128     """
129     def __init__(self, rel, attrs=None):
130         super(ManyToManyRawIdWidget, self).__init__(rel, attrs)
131    
132     def render(self, name, value, attrs=None):
133         attrs['class'] = 'vManyToManyRawIdAdminField'
134         if value:
135             value = ','.join([str(v) for v in value])
136         else:
137             value = ''
138         return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
139    
140     def label_for_value(self, value):
141         return ''
142
143     def value_from_datadict(self, data, files, name):
144         value = data.get(name, None)
145         if value and ',' in value:
146             return data[name].split(',')
147         if value:
148             return [value]
149         return None
150    
151     def _has_changed(self, initial, data):
152         if initial is None:
153             initial = []
154         if data is None:
155             data = []
156         if len(initial) != len(data):
157             return True
158         for pk1, pk2 in zip(initial, data):
159             if force_unicode(pk1) != force_unicode(pk2):
160                 return True
161         return False
162
163 class RelatedFieldWidgetWrapper(forms.Widget):
164     """
165     This class is a wrapper to a given widget to add the add icon for the
166     admin interface.
167     """
168     def __init__(self, widget, rel, admin_site):
169         self.is_hidden = widget.is_hidden
170         self.needs_multipart_form = widget.needs_multipart_form
171         self.attrs = widget.attrs
172         self.choices = widget.choices
173         self.widget = widget
174         self.rel = rel
175         # so we can check if the related object is registered with this AdminSite
176         self.admin_site = admin_site
177
178     def __deepcopy__(self, memo):
179         obj = copy.copy(self)
180         obj.widget = copy.deepcopy(self.widget, memo)
181         obj.attrs = self.widget.attrs
182         memo[id(self)] = obj
183         return obj
184
185     def render(self, name, value, *args, **kwargs):
186         rel_to = self.rel.to
187         related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower())
188         self.widget.choices = self.choices
189         output = [self.widget.render(name, value, *args, **kwargs)]
190         if rel_to in self.admin_site._registry: # If the related object has an admin interface:
191             # TODO: "id_" is hard-coded here. This should instead use the correct
192             # API to determine the ID dynamically.
193             output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
194                 (related_url, name))
195             output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>' % settings.ADMIN_MEDIA_PREFIX)
196         return mark_safe(u''.join(output))
197
198     def build_attrs(self, extra_attrs=None, **kwargs):
199         "Helper function for building an attribute dictionary."
200         self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
201         return self.attrs
202
203     def value_from_datadict(self, data, files, name):
204         return self.widget.value_from_datadict(data, files, name)
205
206     def _has_changed(self, initial, data):
207         return self.widget._has_changed(initial, data)
208
209     def id_for_label(self, id_):
210         return self.widget.id_for_label(id_)
Note: See TracBrowser for help on using the browser.