{"id":136,"date":"2010-07-08T15:45:17","date_gmt":"2010-07-08T19:45:17","guid":{"rendered":"http:\/\/dashdrum.com\/blog\/?p=136"},"modified":"2010-07-12T13:26:26","modified_gmt":"2010-07-12T17:26:26","slug":"custom-widget-my-first-experience","status":"publish","type":"post","link":"https:\/\/dashdrum.com\/blog\/2010\/07\/custom-widget-my-first-experience\/","title":{"rendered":"Custom Widget &#8211; My First Experience"},"content":{"rendered":"<p>I hadn&#8217;t needed to write a custom widget before today, but I needed to have disabled options included in a &lt;select&gt; list.  After a little research and a LOT of trial and error, this is what I came up with.<\/p>\n<pre>from django.forms.widgets import Select, force_unicode, flatatt, escape, mark_safe, chain, conditional_escape\r\n\r\nclass SelectDisabled(Select):\r\n    def __init__(self, attrs=None, choices=(), disabled=[]):\r\n        super(SelectDisabled, self).__init__(attrs, choices)\r\n        self.disabled = list(disabled)\r\n\r\n    def render(self, name, value, attrs=None, choices=()):\r\n        if value is None: value = ''\r\n        final_attrs = self.build_attrs(attrs, name=name)\r\n        output = [u'&lt;select%s&gt;' % flatatt(final_attrs)]\r\n        options = self.render_options(choices, [value], self.disabled)\r\n        if options:\r\n            output.append(options)\r\n        output.append('&lt;\/select&gt;')\r\n        return mark_safe(u'\\n'.join(output))\r\n\r\n    def render_options(self, choices, selected_choices, disabled_choices):\r\n        def render_option(option_value, option_label):\r\n            option_value = force_unicode(option_value)\r\n            option_label = (option_value in disabled_choices) and (force_unicode(option_label) + ' - SOLD OUT') or force_unicode(option_label)\r\n            selected_html = (option_value in selected_choices) and u' selected=\"selected\"' or ''\r\n            disabled_html = (option_value in disabled_choices) and u' disabled=\"disabled\"' or ''\r\n            return u'&lt;option value=\"%s\"%s%s>%s&lt;\/option&gt;' % (\r\n                escape(option_value), selected_html, disabled_html,\r\n                conditional_escape(option_label))\r\n        # Normalize to strings.\r\n        selected_choices = set([force_unicode(v) for v in selected_choices])\r\n        disabled_choices = set([force_unicode(v) for v in disabled_choices])\r\n        output = []\r\n        for option_value, option_label in chain(self.choices, choices):\r\n            if isinstance(option_label, (list, tuple)):\r\n                output.append(u'&lt;optgroup label=\"%s\"&gt;' % escape(force_unicode(option_value)))\r\n                for option in option_label:\r\n                    output.append(render_option(*option))\r\n                output.append(u'&lt;\/optgroup&gt;')\r\n            else:\r\n                output.append(render_option(option_value, option_label))\r\n        return u'\\n'.join(output)<\/pre>\n<p>And here&#8217;s how I used it in the form:<\/p>\n<pre>\r\nclass B2CRegistrationStartForm(Form): \r\n    def __init__(self, disabled, *args, **kwargs):\r\n        super(B2CRegistrationStartForm, self).__init__(*args, **kwargs)\r\n        if disabled:\r\n            self.fields['event'].widget.disabled = disabled\r\n    event = ModelChoiceField(widget=SelectDisabled(),queryset=Event.b2c_web_reg.all(),empty_label=None)  \r\n<\/pre>\n<p>And here is the form creation in views.py:<\/p>\n<pre>    disabled = []\r\n    for e in Event.b2c_sold_out.values_list('id'):\r\n        disabled.append(e[0])\r\n    form = B2CRegistrationStartForm( data=request.POST or None, disabled=disabled)<\/pre>\n<p>(The list of disabled IDs needs to be in a list, not a list of tuples)<\/p>\n<p>It works fine, and I learned about custom widgets, but I don&#8217;t feel like I was very Object Oriented in my approach.  The __init__() method uses super() well, but the other two basically replace the methods in the parent class.  When I update to a newer version of Django (from the current 1.0.4), I&#8217;ll have to check this carefully and probably redo it.<\/p>\n<p>Anyone have suggestions on how I could better implement this feature?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I hadn&#8217;t needed to write a custom widget before today, but I needed to have disabled options included in a &lt;select&gt; list. After a little research and a LOT of trial and error, this is what I came up with. from django.forms.widgets import Select, force_unicode, flatatt, escape, mark_safe, chain, conditional_escape class SelectDisabled(Select): def __init__(self, attrs=None, &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/dashdrum.com\/blog\/2010\/07\/custom-widget-my-first-experience\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Custom Widget &#8211; My First Experience&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-136","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/posts\/136","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/comments?post=136"}],"version-history":[{"count":12,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/posts\/136\/revisions"}],"predecessor-version":[{"id":149,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/posts\/136\/revisions\/149"}],"wp:attachment":[{"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/media?parent=136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/categories?post=136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dashdrum.com\/blog\/wp-json\/wp\/v2\/tags?post=136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}