# coding:utf-8 from itertools import chain import xadmin from django import forms from django.db.models import ManyToManyField from django.forms.utils import flatatt from django.template import loader from django.utils.encoding import force_text from django.utils.html import escape, conditional_escape from django.utils.safestring import mark_safe from xadmin.util import vendor from xadmin.views import BaseAdminPlugin, ModelFormAdminView class SelectMultipleTransfer(forms.SelectMultiple): @property def media(self): return vendor('xadmin.widget.select-transfer.js', 'xadmin.widget.select-transfer.css') def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): self.verbose_name = verbose_name self.is_stacked = is_stacked super(SelectMultipleTransfer, self).__init__(attrs, choices) def render_opt(self, selected_choices, option_value, option_label): option_value = force_text(option_value) return u'' % ( escape(option_value), conditional_escape(force_text(option_label))), bool(option_value in selected_choices) def render(self, name, value, attrs=None, choices=()): if attrs is None: attrs = {} attrs['class'] = '' if self.is_stacked: attrs['class'] += 'stacked' if value is None: value = [] final_attrs = self.build_attrs(attrs, extra_attrs={'name': name}) selected_choices = set(force_text(v) for v in value) available_output = [] chosen_output = [] for option_value, option_label in chain(self.choices, choices): if isinstance(option_label, (list, tuple)): available_output.append(u'' % escape(force_text(option_value))) for option in option_label: output, selected = self.render_opt( selected_choices, *option) if selected: chosen_output.append(output) else: available_output.append(output) available_output.append(u'') else: output, selected = self.render_opt( selected_choices, option_value, option_label) if selected: chosen_output.append(output) else: available_output.append(output) context = { 'verbose_name': self.verbose_name, 'attrs': attrs, 'field_id': attrs['id'], 'flatatts': flatatt(final_attrs), 'available_options': u'\n'.join(available_output), 'chosen_options': u'\n'.join(chosen_output), } return mark_safe(loader.render_to_string('xadmin/forms/transfer.html', context)) class SelectMultipleDropdown(forms.SelectMultiple): @property def media(self): return vendor('multiselect.js', 'multiselect.css', 'xadmin.widget.multiselect.js') def render(self, name, value, attrs=None, choices=()): if attrs is None: attrs = {} attrs['class'] = 'selectmultiple selectdropdown' return super(SelectMultipleDropdown, self).render(name, value, attrs, choices) class M2MSelectPlugin(BaseAdminPlugin): def init_request(self, *args, **kwargs): return hasattr(self.admin_view, 'style_fields') and \ ( 'm2m_transfer' in self.admin_view.style_fields.values() or 'm2m_dropdown' in self.admin_view.style_fields.values() ) def get_field_style(self, attrs, db_field, style, **kwargs): if style == 'm2m_transfer' and isinstance(db_field, ManyToManyField): return {'widget': SelectMultipleTransfer(db_field.verbose_name, False), 'help_text': ''} if style == 'm2m_dropdown' and isinstance(db_field, ManyToManyField): return {'widget': SelectMultipleDropdown, 'help_text': ''} return attrs xadmin.site.register_plugin(M2MSelectPlugin, ModelFormAdminView)