From 8550161e531a603d57723850fb09c4c9b7ca60b9 Mon Sep 17 00:00:00 2001 From: Moritz Sichert Date: Wed, 9 Sep 2015 15:09:59 +0200 Subject: [PATCH] Refs #25294 -- Moved BoundField to django.forms.boundfield. --- django/contrib/admin/utils.py | 2 +- django/forms/__init__.py | 1 + django/forms/boundfield.py | 227 ++++++++++++++++++++++++ django/forms/fields.py | 2 +- django/forms/forms.py | 230 +------------------------ django/forms/utils.py | 7 + tests/forms_tests/tests/test_fields.py | 3 + 7 files changed, 245 insertions(+), 227 deletions(-) create mode 100644 django/forms/boundfield.py diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 88e422e40f..2337fe62a5 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -11,7 +11,7 @@ from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector from django.db.models.sql.constants import QUERY_TERMS -from django.forms.forms import pretty_name +from django.forms.utils import pretty_name from django.utils import formats, six, timezone from django.utils.encoding import force_str, force_text, smart_text from django.utils.html import format_html diff --git a/django/forms/__init__.py b/django/forms/__init__.py index e0be2d164f..1c319219a6 100644 --- a/django/forms/__init__.py +++ b/django/forms/__init__.py @@ -3,6 +3,7 @@ Django validation and HTML form handling. """ from django.core.exceptions import ValidationError # NOQA +from django.forms.boundfield import * # NOQA from django.forms.fields import * # NOQA from django.forms.forms import * # NOQA from django.forms.formsets import * # NOQA diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py new file mode 100644 index 0000000000..bcfe2e2a2d --- /dev/null +++ b/django/forms/boundfield.py @@ -0,0 +1,227 @@ +from __future__ import unicode_literals + +import datetime + +from django.forms.utils import flatatt, pretty_name +from django.forms.widgets import Textarea, TextInput +from django.utils import six +from django.utils.encoding import ( + force_text, python_2_unicode_compatible, smart_text, +) +from django.utils.html import conditional_escape, format_html, html_safe +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +__all__ = ('BoundField',) + + +UNSET = object() + + +@html_safe +@python_2_unicode_compatible +class BoundField(object): + "A Field plus data" + def __init__(self, form, field, name): + self.form = form + self.field = field + self.name = name + self.html_name = form.add_prefix(name) + self.html_initial_name = form.add_initial_prefix(name) + self.html_initial_id = form.add_initial_prefix(self.auto_id) + if self.field.label is None: + self.label = pretty_name(name) + else: + self.label = self.field.label + self.help_text = field.help_text or '' + self._initial_value = UNSET + + def __str__(self): + """Renders this field as an HTML widget.""" + if self.field.show_hidden_initial: + return self.as_widget() + self.as_hidden(only_initial=True) + return self.as_widget() + + def __iter__(self): + """ + Yields rendered strings that comprise all widgets in this BoundField. + + This really is only useful for RadioSelect widgets, so that you can + iterate over individual radio buttons in a template. + """ + id_ = self.field.widget.attrs.get('id') or self.auto_id + attrs = {'id': id_} if id_ else {} + for subwidget in self.field.widget.subwidgets(self.html_name, self.value(), attrs): + yield subwidget + + def __len__(self): + return len(list(self.__iter__())) + + def __getitem__(self, idx): + # Prevent unnecessary reevaluation when accessing BoundField's attrs + # from templates. + if not isinstance(idx, six.integer_types): + raise TypeError + return list(self.__iter__())[idx] + + @property + def errors(self): + """ + Returns an ErrorList for this field. Returns an empty ErrorList + if there are none. + """ + return self.form.errors.get(self.name, self.form.error_class()) + + def as_widget(self, widget=None, attrs=None, only_initial=False): + """ + Renders the field by rendering the passed widget, adding any HTML + attributes passed as attrs. If no widget is specified, then the + field's default widget will be used. + """ + if not widget: + widget = self.field.widget + + if self.field.localize: + widget.is_localized = True + + attrs = attrs or {} + if self.field.disabled: + attrs['disabled'] = True + auto_id = self.auto_id + if auto_id and 'id' not in attrs and 'id' not in widget.attrs: + if not only_initial: + attrs['id'] = auto_id + else: + attrs['id'] = self.html_initial_id + + if not only_initial: + name = self.html_name + else: + name = self.html_initial_name + return force_text(widget.render(name, self.value(), attrs=attrs)) + + def as_text(self, attrs=None, **kwargs): + """ + Returns a string of HTML for representing this as an . + """ + return self.as_widget(TextInput(), attrs, **kwargs) + + def as_textarea(self, attrs=None, **kwargs): + "Returns a string of HTML for representing this as a