Django

Code

root/django/trunk/django/contrib/admin/widgets.py

Revision 9444, 10.9 kB (checked in by brosner, 1 week ago)

Fixed #9258 -- Use _default_manager in ForeignKeyRawIdWidget?.label_for_value. Thanks nullie for the patch.

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 jsi18n
21     catalog has been loaded in the page
22     """
23     class Media:
24         js = (settings.ADMIN_MEDIA_PREFIX + "js/core.js",
25               settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",
26               settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js")
27
28     def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
29         self.verbose_name = verbose_name
30         self.is_stacked = is_stacked
31         super(FilteredSelectMultiple, self).__init__(attrs, choices)
32
33     def render(self, name, value, attrs=None, choices=()):
34         output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
35         output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
36         # TODO: "id_" is hard-coded here. This should instead use the correct
37         # API to determine the ID dynamically.
38         output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
39             (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
40         return mark_safe(u''.join(output))
41
42 class AdminDateWidget(forms.TextInput):
43     class Media:
44         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
45               settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
46
47     def __init__(self, attrs={}):
48         super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
49
50 class AdminTimeWidget(forms.TextInput):
51     class Media:
52         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
53               settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
54
55     def __init__(self, attrs={}):
56         super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
57
58 class AdminSplitDateTime(forms.SplitDateTimeWidget):
59     """
60     A SplitDateTime Widget that has some admin-specific styling.
61     """
62     def __init__(self, attrs=None):
63         widgets = [AdminDateWidget, AdminTimeWidget]
64         # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
65         # we want to define widgets.
66         forms.MultiWidget.__init__(self, widgets, attrs)
67
68     def format_output(self, rendered_widgets):
69         return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
70             (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
71
72 class AdminRadioFieldRenderer(RadioFieldRenderer):
73     def render(self):
74         """Outputs a <ul> for this set of radio fields."""
75         return mark_safe(u'<ul%s>\n%s\n</ul>' % (
76             flatatt(self.attrs),
77             u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
78         )
79
80 class AdminRadioSelect(forms.RadioSelect):
81     renderer = AdminRadioFieldRenderer
82
83 class AdminFileWidget(forms.FileInput):
84     """
85     A FileField Widget that shows its current value if it has one.
86     """
87     def __init__(self, attrs={}):
88         super(AdminFileWidget, self).__init__(attrs)
89
90     def render(self, name, value, attrs=None):
91         output = []
92         if value and hasattr(value, "url"):
93             output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \
94                 (_('Currently:'), value.url, value, _('Change:')))
95         output.append(super(AdminFileWidget, self).render(name, value, attrs))
96         return mark_safe(u''.join(output))
97
98 class ForeignKeyRawIdWidget(forms.TextInput):
99     """
100     A Widget for displaying ForeignKeys in the "raw_id" interface rather than
101     in a <select> box.
102     """
103     def __init__(self, rel, attrs=None):
104         self.rel = rel
105         super(ForeignKeyRawIdWidget, self).__init__(attrs)
106
107     def render(self, name, value, attrs=None):
108         if attrs is None:
109             attrs = {}
110         related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
111         params = self.url_parameters()
112         if params:
113             url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
114         else:
115             url = ''
116         if not attrs.has_key('class'):
117             attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
118         output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
119         # TODO: "id_" is hard-coded here. This should instead use the correct
120         # API to determine the ID dynamically.
121         output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
122             (related_url, url, name))
123         output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup')))
124         if value:
125             output.append(self.label_for_value(value))
126         return mark_safe(u''.join(output))
127    
128     def base_url_parameters(self):
129         params = {}
130         if self.rel.limit_choices_to:
131             items = []
132             for k, v in self.rel.limit_choices_to.items():
133                 if isinstance(v, list):
134                     v = [str(x) for x in v]
135                 else:
136                     v = str(v)
137                 items.append((k, ','.join(v)))
138             params.update(dict(items))
139         return params   
140    
141     def url_parameters(self):
142         from django.contrib.admin.views.main import TO_FIELD_VAR
143         params = self.base_url_parameters()
144         params.update({TO_FIELD_VAR: self.rel.get_related_field().name})
145         return params
146            
147     def label_for_value(self, value):
148         key = self.rel.get_related_field().name
149         obj = self.rel.to._default_manager.get(**{key: value})
150         return '&nbsp;<strong>%s</strong>' % truncate_words(obj, 14)
151
152 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
153     """
154     A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
155     in a <select multiple> box.
156     """
157     def __init__(self, rel, attrs=None):
158         super(ManyToManyRawIdWidget, self).__init__(rel, attrs)
159
160     def render(self, name, value, attrs=None):
161         attrs['class'] = 'vManyToManyRawIdAdminField'
162         if value:
163             value = ','.join([str(v) for v in value])
164         else:
165             value = ''
166         return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
167    
168     def url_parameters(self):
169         return self.base_url_parameters()
170    
171     def label_for_value(self, value):
172         return ''
173
174     def value_from_datadict(self, data, files, name):
175         value = data.get(name, None)
176         if value and ',' in value:
177             return data[name].split(',')
178         if value:
179             return [value]
180         return None
181
182     def _has_changed(self, initial, data):
183         if initial is None:
184             initial = []
185         if data is None:
186             data = []
187         if len(initial) != len(data):
188             return True
189         for pk1, pk2 in zip(initial, data):
190             if force_unicode(pk1) != force_unicode(pk2):
191                 return True
192         return False
193
194 class RelatedFieldWidgetWrapper(forms.Widget):
195     """
196     This class is a wrapper to a given widget to add the add icon for the
197     admin interface.
198     """
199     def __init__(self, widget, rel, admin_site):
200         self.is_hidden = widget.is_hidden
201         self.needs_multipart_form = widget.needs_multipart_form
202         self.attrs = widget.attrs
203         self.choices = widget.choices
204         self.widget = widget
205         self.rel = rel
206         # so we can check if the related object is registered with this AdminSite
207         self.admin_site = admin_site
208
209     def __deepcopy__(self, memo):
210         obj = copy.copy(self)
211         obj.widget = copy.deepcopy(self.widget, memo)
212         obj.attrs = self.widget.attrs
213         memo[id(self)] = obj
214         return obj
215
216     def _media(self):
217         return self.widget.media
218     media = property(_media)
219
220     def render(self, name, value, *args, **kwargs):
221         rel_to = self.rel.to
222         related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower())
223         self.widget.choices = self.choices
224         output = [self.widget.render(name, value, *args, **kwargs)]
225         if rel_to in self.admin_site._registry: # If the related object has an admin interface:
226             # TODO: "id_" is hard-coded here. This should instead use the correct
227             # API to determine the ID dynamically.
228             output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
229                 (related_url, name))
230             output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
231         return mark_safe(u''.join(output))
232
233     def build_attrs(self, extra_attrs=None, **kwargs):
234         "Helper function for building an attribute dictionary."
235         self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
236         return self.attrs
237
238     def value_from_datadict(self, data, files, name):
239         return self.widget.value_from_datadict(data, files, name)
240
241     def _has_changed(self, initial, data):
242         return self.widget._has_changed(initial, data)
243
244     def id_for_label(self, id_):
245         return self.widget.id_for_label(id_)
246
247 class AdminTextareaWidget(forms.Textarea):
248     def __init__(self, attrs=None):
249         final_attrs = {'class': 'vLargeTextField'}
250         if attrs is not None:
251             final_attrs.update(attrs)
252         super(AdminTextareaWidget, self).__init__(attrs=final_attrs)
253
254 class AdminTextInputWidget(forms.TextInput):
255     def __init__(self, attrs=None):
256         final_attrs = {'class': 'vTextField'}
257         if attrs is not None:
258             final_attrs.update(attrs)
259         super(AdminTextInputWidget, self).__init__(attrs=final_attrs)
260
261 class AdminURLFieldWidget(forms.TextInput):
262     def __init__(self, attrs=None):
263         final_attrs = {'class': 'vURLField'}
264         if attrs is not None:
265             final_attrs.update(attrs)
266         super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
267
268 class AdminIntegerFieldWidget(forms.TextInput):
269     def __init__(self, attrs=None):
270         final_attrs = {'class': 'vIntegerField'}
271         if attrs is not None:
272             final_attrs.update(attrs)
273         super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
274
275 class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
276     def __init__(self, attrs=None):
277         final_attrs = {'class': 'vCommaSeparatedIntegerField'}
278         if attrs is not None:
279             final_attrs.update(attrs)
280         super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs)
Note: See TracBrowser for help on using the browser.