Django中在模板上通过{{form}}即自动可生成表单内容,提供了非常便捷和强大的功能。这种功能虽然强大,但却不能满足很多的需求,比如一个表单的内容非常多,在页面上希望通过Tab将信息"分门别类"的隔开,Django内置的功能就无能为力了。
好在Django是开源产品,可以通过修改其代码,为其增加自定义输出字段的能力,即可很方便的达到目的。主要修改form的.as_table,as_ul,as_p等方法,为其增加一个待渲染字段列表即可。为了确保Django后续平滑升级,一般我们不会直接修改Django代码,借助Python的多重继承机制,可以非常轻松的为Django打上这个补丁。需要具备这种能力的form只需要先集成自定义的这个混入类(Mixin),然后再按照原有的方式继承form即可。
如:
UserForm(PartialRenderFormMixin,forms.ModelForm): class Meta: model = User def render_baseinfo(self): return self.as_table(('username','first_name','last_name')) def render_extinfo(self): return self.as_table(('email',))
PartialRenderFormMixin代码:
class PartialRenderFormMixin(): def _html_output(self,render_fields, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." top_errors = self.non_field_errors() # Errors that should be displayed above all fields. output, hidden_fields = [], [] for name, field in self.fields.items(): if render_fields and not name in render_fields: continue html_class_attr = '' bf = self[name] bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. if bf.is_hidden: if bf_errors: top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) hidden_fields.append(unicode(bf)) else: # Create a 'class="..."' atribute if the row should have any # CSS classes applied. css_classes = bf.css_classes() if css_classes: html_class_attr = ' class="%s"' % css_classes if errors_on_separate_row and bf_errors: output.append(error_row % force_unicode(bf_errors)) if bf.label: label = conditional_escape(force_unicode(bf.label)) # Only add the suffix if the label does not end in # punctuation. if self.label_suffix: if label[-1] not in ':?.!': label += self.label_suffix label = bf.label_tag(label) or '' else: label = '' if field.help_text: help_text = help_text_html % force_unicode(field.help_text) else: help_text = u'' output.append(normal_row % { 'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text, 'html_class_attr': html_class_attr }) if top_errors: output.insert(0, error_row % force_unicode(top_errors)) if hidden_fields: # Insert any hidden fields in the last row. str_hidden = u''.join(hidden_fields) if output: last_row = output[-1] # Chop off the trailing row_ender (e.g. '</td></tr>') and # insert the hidden fields. if not last_row.endswith(row_ender): # This can happen in the as_p() case (and possibly others # that users write): if there are only top errors, we may # not be able to conscript the last row for our purposes, # so insert a new, empty row. last_row = (normal_row % {'errors': '', 'label': '', 'field': '', 'help_text':'', 'html_class_attr': html_class_attr}) output.append(last_row) output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender else: # If there aren't any rows in the output, just append the # hidden fields. output.append(str_hidden) return mark_safe(u'\n'.join(output)) def as_table(self,render_fields=None): "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." return self._html_output( render_fields, normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row = u'<tr><td colspan="2">%s</td></tr>', row_ender = u'</td></tr>', help_text_html = u'<br /><span class="helptext">%s</span>', errors_on_separate_row = False) def as_bootstrip(self,render_fields =None): output = [] for name, field in self.fields.items(): if render_fields and not name in render_fields: continue f = BoundField(self,field,name) output.append( as_bootstrap(f)) return mark_safe(u''.join(output)) def as_ul(self,render_fields=None): "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." return self._html_output( render_fields, normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', error_row = u'<li>%s</li>', row_ender = '</li>', help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = False) def as_p(self,render_fields=None): "Returns this form rendered as HTML <p>s." return self._html_output( render_fields, normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', error_row = u'%s', row_ender = '</p>', help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = True)
下面的方法是配合bootstrap_tookit使用的,可以方便的与bootstrapt集成:
def as_bootstrip(self,render_fields =None): output = [] for name, field in self.fields.items(): if render_fields and not name in render_fields: continue f = BoundField(self,field,name) output.append( as_bootstrap(f)) return mark_safe(u''.join(output))