python - Django custom widget displaying escaped html -


i wrote widget django forms in order have bootstrap3 multiple checkbox directly in template in order reuse in future.

so wrote app prettyforms contains basic files (__init__.py, views.py ...) , created forms.py wrote custom widget.

the main form view item/forms.py such as

from django import forms django.utils.translation import ugettext _ mysite.item.models import item, itemcategory  mysite.prettyforms import forms prettyforms  class createitemform(forms.modelform):      class meta:         model = item         fields = (             'categories',         )         widgets = {             'categories': prettyforms.prettycheckboxselectmultiple(),         }      def __init__(self, *args, **kwargs):         self.request = kwargs.pop('request', none)         super(createitemform, self).__init__(*args, **kwargs)          categories = itemcategory.objects.all()         categories_choices = [ [category.pk, category.name] category in categories ]          self.fields['categories'].choices = categories_choices         self.fields['categories'].error_messages = {             'required': _('at least 1 category must selected')         } 

and file containing widget prettyforms/forms.py such as:

# -*- coding: utf-8 -*-  django.forms.widgets import (     choiceinput, selectmultiple, renderermixin,     checkboxchoiceinput, choicefieldrenderer ) django.utils.encoding import (     force_str, force_text, python_2_unicode_compatible, ) django.utils.safestring import mark_safe django.forms.utils import flatatt django.utils.html import format_html, html_safe  @html_safe @python_2_unicode_compatible class prettychoiceinput(choiceinput):      def __str__(self):         return self.render()      def render(self, name=none, value=none, attrs=none, choices=()):         # note: not sure if need compute don't use         if self.id_for_label:             label_for = format_html(' for="{}"', self.id_for_label)         else:             label_for = ''         attrs = dict(self.attrs, **attrs) if attrs else self.attrs          # todo: create css btn-checkbox convention         return format_html(             '<label class="btn btn-primary">{} {}</label>',             self.tag(attrs),             self.choice_label         )      def tag(self, attrs=none):         attrs = attrs or self.attrs         final_attrs = dict(attrs, type=self.input_type, name=self.name, value=self.choice_value)         if self.is_checked():             final_attrs['checked'] = 'checked'         # added autocomplete off         return format_html('<input{} autocomplete="off" />', flatatt(final_attrs))   class prettycheckboxchoiceinput(prettychoiceinput):     input_type = 'checkbox'      def __init__(self, *args, **kwargs):         super(prettycheckboxchoiceinput, self).__init__(*args, **kwargs)         self.value = set(force_text(v) v in self.value)      def is_checked(self):         return self.choice_value in self.value   @html_safe @python_2_unicode_compatible class prettychoicefieldrenderer(choicefieldrenderer):     outer_html = '<div class="btn-group" data-toggle="buttons"{id_attr}>{content}</div>'     inner_html = '{choice_value}{sub_widgets}'      def __getitem__(self, idx):         choice = self.choices[idx]  # let indexerror propagate         return self.choice_input_class(self.name, self.value, self.attrs.copy(), choice, idx)      def __str__(self):         return self.render()      def render(self):         """         outputs <ul> set of choice fields.         if id given field, applied <ul> (each         item in list id of `$id_$i`).         """         id_ = self.attrs.get('id', none)         output = []         i, choice in enumerate(self.choices):             choice_value, choice_label = choice             if isinstance(choice_label, (tuple, list)):                 attrs_plus = self.attrs.copy()                 if id_:                     attrs_plus['id'] += '_{}'.format(i)                 sub_ul_renderer = choicefieldrenderer(name=self.name,                                                       value=self.value,                                                       attrs=attrs_plus,                                                       choices=choice_label)                 sub_ul_renderer.choice_input_class = self.choice_input_class                 output.append(format_html(self.inner_html, choice_value=choice_value,                                           sub_widgets=sub_ul_renderer.render()))             else:                 w = self.choice_input_class(self.name, self.value,                                             self.attrs.copy(), choice, i)                 output.append(format_html(self.inner_html,                                           choice_value=force_text(w), sub_widgets=''))         return format_html(self.outer_html,                            id_attr=format_html(' id="{}"', id_) if id_ else '',                            content=mark_safe('\n'.join(output)))   class prettycheckboxfieldrenderer(prettychoicefieldrenderer):     choice_input_class = prettycheckboxchoiceinput   class prettycheckboxselectmultiple(renderermixin, selectmultiple):     renderer = prettycheckboxfieldrenderer     _empty_value = [] 

most of text in classes , methods copied , pasted widgets.py

in templates put

{{ form.as_p }} 

and problem fields rendered in html 'categories' field escaped. inspecting html results inner_html prettychoicefieldrenderer escaped while outer_html not means html not escaped template system widget renderer before. don't know where. that's asking help, if can spot incorrect.

this html of 'categories' field

as can see outer_html not escaped inner_html is

<p>     <label for="id_categories_0">categories:</label>     <div class="btn-group" data-toggle="buttons" id="id_categories">          &lt;label class=&quot;btn btn-primary&quot;&gt;&lt;input id=&quot;id_categories_0&quot; name=&quot;categories&quot; type=&quot;checkbox&quot; value=&quot;1&quot; autocomplete=&quot;off&quot; /&gt; electronics&lt;/label&gt;&lt;label class=&quot;btn         btn-primary&quot;&gt;&lt;input id=&quot;id_categories_1&quot; name=&quot;categories&quot; type=&quot;checkbox&quot; value=&quot;2&quot; autocomplete=&quot;off&quot; /&gt; bedroom , productivity&lt;/label&gt; &lt;label class=&quot;btn btn-primary&quot;&gt;&lt;input         id=&quot;id_categories_2&quot; name=&quot;categories&quot; type=&quot;checkbox&quot; value=&quot;3&quot; autocomplete=&quot;off&quot; /&gt; phone&lt;/label&gt; &lt;label class=&quot;btn btn-primary&quot;&gt;&lt;input id=&quot;id_categories_3&quot;         name=&quot;categories&quot; type=&quot;checkbox&quot; value=&quot;4&quot; autocomplete=&quot;off&quot; /&gt; office&lt;/label&gt; &lt;label class=&quot;btn btn-primary&quot;&gt;&lt;input id=&quot;id_categories_4&quot; name=&quot;categories&quot;         type=&quot;checkbox&quot; value=&quot;6&quot; autocomplete=&quot;off&quot; /&gt; kitchen&lt;/label&gt;      </div> </p> 

thank in advance

alright, fixed issue. removed force_text in

output.append(format_html(                            self.inner_html,                            choice_value=force_text(w),                            sub_widgets='' )) 

so gives

output.append(format_html(                            self.inner_html,                            choice_value=w,                            sub_widgets='' )) 

there must reason why django has force_text in https://github.com/django/django/blob/master/django/forms/widgets.py#l728


Comments

Popular posts from this blog

Email notification in google apps script -

c++ - Difference between pre and post decrement in recursive function argument -

javascript - IE11 incompatibility with jQuery's 'readonly'? -