multiselect.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. # coding:utf-8
  2. from itertools import chain
  3. import xadmin
  4. from django import forms
  5. from django.db.models import ManyToManyField
  6. from django.forms.utils import flatatt
  7. from django.template import loader
  8. from django.utils.encoding import force_text
  9. from django.utils.html import escape, conditional_escape
  10. from django.utils.safestring import mark_safe
  11. from xadmin.util import vendor
  12. from xadmin.views import BaseAdminPlugin, ModelFormAdminView
  13. class SelectMultipleTransfer(forms.SelectMultiple):
  14. @property
  15. def media(self):
  16. return vendor('xadmin.widget.select-transfer.js', 'xadmin.widget.select-transfer.css')
  17. def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
  18. self.verbose_name = verbose_name
  19. self.is_stacked = is_stacked
  20. super(SelectMultipleTransfer, self).__init__(attrs, choices)
  21. def render_opt(self, selected_choices, option_value, option_label):
  22. option_value = force_text(option_value)
  23. return u'<option value="%s">%s</option>' % (
  24. escape(option_value), conditional_escape(force_text(option_label))), bool(option_value in selected_choices)
  25. def render(self, name, value, attrs=None, choices=()):
  26. if attrs is None:
  27. attrs = {}
  28. attrs['class'] = ''
  29. if self.is_stacked:
  30. attrs['class'] += 'stacked'
  31. if value is None:
  32. value = []
  33. final_attrs = self.build_attrs(attrs, extra_attrs={'name': name})
  34. selected_choices = set(force_text(v) for v in value)
  35. available_output = []
  36. chosen_output = []
  37. for option_value, option_label in chain(self.choices, choices):
  38. if isinstance(option_label, (list, tuple)):
  39. available_output.append(u'<optgroup label="%s">' %
  40. escape(force_text(option_value)))
  41. for option in option_label:
  42. output, selected = self.render_opt(
  43. selected_choices, *option)
  44. if selected:
  45. chosen_output.append(output)
  46. else:
  47. available_output.append(output)
  48. available_output.append(u'</optgroup>')
  49. else:
  50. output, selected = self.render_opt(
  51. selected_choices, option_value, option_label)
  52. if selected:
  53. chosen_output.append(output)
  54. else:
  55. available_output.append(output)
  56. context = {
  57. 'verbose_name': self.verbose_name,
  58. 'attrs': attrs,
  59. 'field_id': attrs['id'],
  60. 'flatatts': flatatt(final_attrs),
  61. 'available_options': u'\n'.join(available_output),
  62. 'chosen_options': u'\n'.join(chosen_output),
  63. }
  64. return mark_safe(loader.render_to_string('xadmin/forms/transfer.html', context))
  65. class SelectMultipleDropdown(forms.SelectMultiple):
  66. @property
  67. def media(self):
  68. return vendor('multiselect.js', 'multiselect.css', 'xadmin.widget.multiselect.js')
  69. def render(self, name, value, attrs=None, choices=()):
  70. if attrs is None:
  71. attrs = {}
  72. attrs['class'] = 'selectmultiple selectdropdown'
  73. return super(SelectMultipleDropdown, self).render(name, value, attrs, choices)
  74. class M2MSelectPlugin(BaseAdminPlugin):
  75. def init_request(self, *args, **kwargs):
  76. return hasattr(self.admin_view, 'style_fields') and \
  77. (
  78. 'm2m_transfer' in self.admin_view.style_fields.values() or
  79. 'm2m_dropdown' in self.admin_view.style_fields.values()
  80. )
  81. def get_field_style(self, attrs, db_field, style, **kwargs):
  82. if style == 'm2m_transfer' and isinstance(db_field, ManyToManyField):
  83. return {'widget': SelectMultipleTransfer(db_field.verbose_name, False), 'help_text': ''}
  84. if style == 'm2m_dropdown' and isinstance(db_field, ManyToManyField):
  85. return {'widget': SelectMultipleDropdown, 'help_text': ''}
  86. return attrs
  87. xadmin.site.register_plugin(M2MSelectPlugin, ModelFormAdminView)