Hello site
[auf_framonde.git] / eggs / Django-1.4.5-py2.7.egg / django / contrib / admin / widgets.py
1 """
2 Form Widget classes specific to the Django admin site.
3 """
4
5 import copy
6 from django import forms
7 from django.contrib.admin.templatetags.admin_static import static
8 from django.core.urlresolvers import reverse
9 from django.forms.widgets import RadioFieldRenderer
10 from django.forms.util import flatatt
11 from django.utils.html import escape
12 from django.utils.text import Truncator
13 from django.utils.translation import ugettext as _
14 from django.utils.safestring import mark_safe
15 from django.utils.encoding import force_unicode
16
17
18 class FilteredSelectMultiple(forms.SelectMultiple):
19 """
20 A SelectMultiple with a JavaScript filter interface.
21
22 Note that the resulting JavaScript assumes that the jsi18n
23 catalog has been loaded in the page
24 """
25 @property
26 def media(self):
27 js = ["core.js", "SelectBox.js", "SelectFilter2.js"]
28 return forms.Media(js=[static("admin/js/%s" % path) for path in js])
29
30 def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
31 self.verbose_name = verbose_name
32 self.is_stacked = is_stacked
33 super(FilteredSelectMultiple, self).__init__(attrs, choices)
34
35 def render(self, name, value, attrs=None, choices=()):
36 if attrs is None:
37 attrs = {}
38 attrs['class'] = 'selectfilter'
39 if self.is_stacked:
40 attrs['class'] += 'stacked'
41 output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
42 output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
43 # TODO: "id_" is hard-coded here. This should instead use the correct
44 # API to determine the ID dynamically.
45 output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
46 % (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/')))
47 return mark_safe(u''.join(output))
48
49 class AdminDateWidget(forms.DateInput):
50
51 @property
52 def media(self):
53 js = ["calendar.js", "admin/DateTimeShortcuts.js"]
54 return forms.Media(js=[static("admin/js/%s" % path) for path in js])
55
56 def __init__(self, attrs=None, format=None):
57 final_attrs = {'class': 'vDateField', 'size': '10'}
58 if attrs is not None:
59 final_attrs.update(attrs)
60 super(AdminDateWidget, self).__init__(attrs=final_attrs, format=format)
61
62 class AdminTimeWidget(forms.TimeInput):
63
64 @property
65 def media(self):
66 js = ["calendar.js", "admin/DateTimeShortcuts.js"]
67 return forms.Media(js=[static("admin/js/%s" % path) for path in js])
68
69 def __init__(self, attrs=None, format=None):
70 final_attrs = {'class': 'vTimeField', 'size': '8'}
71 if attrs is not None:
72 final_attrs.update(attrs)
73 super(AdminTimeWidget, self).__init__(attrs=final_attrs, format=format)
74
75 class AdminSplitDateTime(forms.SplitDateTimeWidget):
76 """
77 A SplitDateTime Widget that has some admin-specific styling.
78 """
79 def __init__(self, attrs=None):
80 widgets = [AdminDateWidget, AdminTimeWidget]
81 # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
82 # we want to define widgets.
83 forms.MultiWidget.__init__(self, widgets, attrs)
84
85 def format_output(self, rendered_widgets):
86 return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
87 (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
88
89 class AdminRadioFieldRenderer(RadioFieldRenderer):
90 def render(self):
91 """Outputs a <ul> for this set of radio fields."""
92 return mark_safe(u'<ul%s>\n%s\n</ul>' % (
93 flatatt(self.attrs),
94 u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
95 )
96
97 class AdminRadioSelect(forms.RadioSelect):
98 renderer = AdminRadioFieldRenderer
99
100 class AdminFileWidget(forms.ClearableFileInput):
101 template_with_initial = (u'<p class="file-upload">%s</p>'
102 % forms.ClearableFileInput.template_with_initial)
103 template_with_clear = (u'<span class="clearable-file-input">%s</span>'
104 % forms.ClearableFileInput.template_with_clear)
105
106 def url_params_from_lookup_dict(lookups):
107 """
108 Converts the type of lookups specified in a ForeignKey limit_choices_to
109 attribute to a dictionary of query parameters
110 """
111 params = {}
112 if lookups and hasattr(lookups, 'items'):
113 items = []
114 for k, v in lookups.items():
115 if isinstance(v, (tuple, list)):
116 v = u','.join([str(x) for x in v])
117 elif isinstance(v, bool):
118 # See django.db.fields.BooleanField.get_prep_lookup
119 v = ('0', '1')[v]
120 else:
121 v = unicode(v)
122 items.append((k, v))
123 params.update(dict(items))
124 return params
125
126 class ForeignKeyRawIdWidget(forms.TextInput):
127 """
128 A Widget for displaying ForeignKeys in the "raw_id" interface rather than
129 in a <select> box.
130 """
131 def __init__(self, rel, admin_site, attrs=None, using=None):
132 self.rel = rel
133 self.admin_site = admin_site
134 self.db = using
135 super(ForeignKeyRawIdWidget, self).__init__(attrs)
136
137 def render(self, name, value, attrs=None):
138 rel_to = self.rel.to
139 if attrs is None:
140 attrs = {}
141 extra = []
142 if rel_to in self.admin_site._registry:
143 # The related object is registered with the same AdminSite
144 related_url = reverse('admin:%s_%s_changelist' %
145 (rel_to._meta.app_label,
146 rel_to._meta.module_name),
147 current_app=self.admin_site.name)
148
149 params = self.url_parameters()
150 if params:
151 url = u'?' + u'&amp;'.join([u'%s=%s' % (k, v) for k, v in params.items()])
152 else:
153 url = u''
154 if "class" not in attrs:
155 attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
156 # TODO: "lookup_id_" is hard-coded here. This should instead use
157 # the correct API to determine the ID dynamically.
158 extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
159 % (related_url, url, name))
160 extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
161 % (static('admin/img/selector-search.gif'), _('Lookup')))
162 output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
163 if value:
164 output.append(self.label_for_value(value))
165 return mark_safe(u''.join(output))
166
167 def base_url_parameters(self):
168 return url_params_from_lookup_dict(self.rel.limit_choices_to)
169
170 def url_parameters(self):
171 from django.contrib.admin.views.main import TO_FIELD_VAR
172 params = self.base_url_parameters()
173 params.update({TO_FIELD_VAR: self.rel.get_related_field().name})
174 return params
175
176 def label_for_value(self, value):
177 key = self.rel.get_related_field().name
178 try:
179 obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
180 return '&nbsp;<strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...'))
181 except (ValueError, self.rel.to.DoesNotExist):
182 return ''
183
184 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
185 """
186 A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
187 in a <select multiple> box.
188 """
189 def render(self, name, value, attrs=None):
190 if attrs is None:
191 attrs = {}
192 if self.rel.to in self.admin_site._registry:
193 # The related object is registered with the same AdminSite
194 attrs['class'] = 'vManyToManyRawIdAdminField'
195 if value:
196 value = ','.join([force_unicode(v) for v in value])
197 else:
198 value = ''
199 return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
200
201 def url_parameters(self):
202 return self.base_url_parameters()
203
204 def label_for_value(self, value):
205 return ''
206
207 def value_from_datadict(self, data, files, name):
208 value = data.get(name)
209 if value:
210 return value.split(',')
211
212 def _has_changed(self, initial, data):
213 if initial is None:
214 initial = []
215 if data is None:
216 data = []
217 if len(initial) != len(data):
218 return True
219 for pk1, pk2 in zip(initial, data):
220 if force_unicode(pk1) != force_unicode(pk2):
221 return True
222 return False
223
224 class RelatedFieldWidgetWrapper(forms.Widget):
225 """
226 This class is a wrapper to a given widget to add the add icon for the
227 admin interface.
228 """
229 def __init__(self, widget, rel, admin_site, can_add_related=None):
230 self.is_hidden = widget.is_hidden
231 self.needs_multipart_form = widget.needs_multipart_form
232 self.attrs = widget.attrs
233 self.choices = widget.choices
234 self.widget = widget
235 self.rel = rel
236 # Backwards compatible check for whether a user can add related
237 # objects.
238 if can_add_related is None:
239 can_add_related = rel.to in admin_site._registry
240 self.can_add_related = can_add_related
241 # so we can check if the related object is registered with this AdminSite
242 self.admin_site = admin_site
243
244 def __deepcopy__(self, memo):
245 obj = copy.copy(self)
246 obj.widget = copy.deepcopy(self.widget, memo)
247 obj.attrs = self.widget.attrs
248 memo[id(self)] = obj
249 return obj
250
251 @property
252 def media(self):
253 return self.widget.media
254
255 def render(self, name, value, *args, **kwargs):
256 rel_to = self.rel.to
257 info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
258 self.widget.choices = self.choices
259 output = [self.widget.render(name, value, *args, **kwargs)]
260 if self.can_add_related:
261 related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
262 # TODO: "add_id_" is hard-coded here. This should instead use the
263 # correct API to determine the ID dynamically.
264 output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
265 % (related_url, name))
266 output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
267 % (static('admin/img/icon_addlink.gif'), _('Add Another')))
268 return mark_safe(u''.join(output))
269
270 def build_attrs(self, extra_attrs=None, **kwargs):
271 "Helper function for building an attribute dictionary."
272 self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
273 return self.attrs
274
275 def value_from_datadict(self, data, files, name):
276 return self.widget.value_from_datadict(data, files, name)
277
278 def _has_changed(self, initial, data):
279 return self.widget._has_changed(initial, data)
280
281 def id_for_label(self, id_):
282 return self.widget.id_for_label(id_)
283
284 class AdminTextareaWidget(forms.Textarea):
285 def __init__(self, attrs=None):
286 final_attrs = {'class': 'vLargeTextField'}
287 if attrs is not None:
288 final_attrs.update(attrs)
289 super(AdminTextareaWidget, self).__init__(attrs=final_attrs)
290
291 class AdminTextInputWidget(forms.TextInput):
292 def __init__(self, attrs=None):
293 final_attrs = {'class': 'vTextField'}
294 if attrs is not None:
295 final_attrs.update(attrs)
296 super(AdminTextInputWidget, self).__init__(attrs=final_attrs)
297
298 class AdminURLFieldWidget(forms.TextInput):
299 def __init__(self, attrs=None):
300 final_attrs = {'class': 'vURLField'}
301 if attrs is not None:
302 final_attrs.update(attrs)
303 super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
304
305 class AdminIntegerFieldWidget(forms.TextInput):
306 def __init__(self, attrs=None):
307 final_attrs = {'class': 'vIntegerField'}
308 if attrs is not None:
309 final_attrs.update(attrs)
310 super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
311
312 class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
313 def __init__(self, attrs=None):
314 final_attrs = {'class': 'vCommaSeparatedIntegerField'}
315 if attrs is not None:
316 final_attrs.update(attrs)
317 super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs)