PageRenderTime 66ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/django/contrib/admin/widgets.py

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