def get_field_tuple(name, form_or_model): """Returns a tuple for the field, of given instance, identified by "name". Instance could be a model instance, a form instance or any arbitrary object. The returned tuple is in the form: (label, attrs, value) """ name, sep, suffix = name.partition(':') label = "" value = "" td_attrs = {} field_list = get_fields(form_or_model) field = None if name in field_list: field = field_list[name] elif hasattr(form_or_model, name): field = getattr(form_or_model, name) if hasattr(field, 'short_description'): name = field.short_description if isinstance(field, models.Field): label = '%s:' % field.verbose_name value = '%s' % field_to_string(field, form_or_model) elif isinstance(field, forms.Field): bf = BoundField(form_or_model, field, name) label = '%s' % bf.label_tag() value = '%s' % bf if bf.help_text: value += '<br/><span title="%(help_text)s" class="helptext helppopup">%(help_text)s</span>' % { "help_text": '%s' % bf.help_text } errors = bf.errors if errors: value += '<br/>\n<ul class="errorlist">\n' for error in errors: value += '\t<li>%s</li>\n' % error value += '</ul>\n' css_classes = bf.css_classes() if css_classes: td_attrs['class'] = css_classes else: name = _(pretty_name(name).lower()) label = '%s:' % name.capitalize() value = field() if callable(field) else field firstcap_label = label[:1].upper() + label[1:] if suffix: value += " " + suffix return mark_safe(firstcap_label), flatatt(td_attrs), mark_safe(value)
def form_input_text(form: forms.Form, field: BoundField, input_type: str = "text") -> str: """Form group with label, input, and errors when form is bound.""" form_is_bound = form.is_bound errors = field.errors has_errors = form_is_bound and errors return format_html( """ <div class="form-group"> <label for="{input_id}">{label}</label> <input type="{input_type}" class="form-control {invalid_class}" id="{input_id}" name="{name}" value="{value}" aria-describedby="{input_id}-help {input_id}-feedback" required> <small id="{input_id}-help" class="form-text text-muted">{help_text}</small> <div id="{input_id}-feedback" class="invalid-feedback">{errors}</div> </div> """, input_id=field.id_for_label, label=field.label, input_type=input_type, invalid_class="is-invalid" if has_errors else "", has_errors=has_errors, name=field.html_name, value=field.value() or "", help_text=field.help_text, errors=" ".join(field.errors), )
def as_widget(self, widget=None, attrs=None, only_initial=False): subfield_widgets = [] for i, subfield in enumerate(self.field.fields): sub_bf = BoundField(self.form, subfield, "{}_{}".format(self.name, i)) subfield_widgets.append(self._subfield_as_widget(sub_bf)) return render_to_string("dynamic_forms/widgets/formrender.html", context={'subfields': subfield_widgets})
def test_get_tuple_for_form_field(self): """Tests returning a tuple for a form instance's field. """ from django.forms.boundfield import BoundField b = BoundField(self.f, self.f.fields['username'], 'username') self.assertEqual(get_field_tuple("username", self.f), ( '<label class="required" for="id_username">Username:</label>', ' class="required"', '%s<br/><span title="Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" class="helptext helppopup">Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters</span>' % (b, ))) b = BoundField(self.f, self.f.fields['email'], 'email') self.assertEqual( get_field_tuple("email", self.f), ('<label class="required" for="id_email">Email:</label>', ' class="required"', '%s' % (b, ))) b = BoundField(self.f, self.f.fields['password2'], 'password2') self.assertEqual(get_field_tuple("password2", self.f), ( '<label for="id_password2">Password confirmation:</label>', ' class="errors"', '%s<br/><span title="%s" class="helptext helppopup">%s</span><br/>\n<ul class="errorlist">\n\t<li>This field is required.</li>\n</ul>\n' % (b, b.help_text, b.help_text)))
def test_crispy_field_and_class_converters(): template = Template(""" {% load crispy_forms_field %} {% crispy_field testField 'class' 'error' %} """) test_form = SampleForm() field_instance = test_form.fields["email"] bound_field = BoundField(test_form, field_instance, "email") c = Context({"testField": bound_field}) html = template.render(c) assert "error" in html assert "inputtext" in html
def field_(self, name): """ From https://github.com/halfnibble/django-underscore-filters Get a form field starting with _. Taken near directly from Django > forms. Returns a BoundField with the given name. """ try: field = self.fields[name] except KeyError as e: raise KeyError("Key %r not found in '%s'" % (name, self.__class__.__name__)) from e return BoundField(self, field, name)
def render_form_field(field: BoundField, switch: bool = False, add_choices: choice_list_type = None, replace_choices: choice_list_type = None, empty_option: Optional[str] = None, value: field_value = None): if value: field.initial = value return { 'field': field, 'switch': switch, 'add_choices': add_choices, 'replace_choices': replace_choices, 'empty_option': empty_option, 'value': value, # TODO: radio values? }
def test_crispy_addon(settings): test_form = SampleForm() field_instance = test_form.fields["email"] bound_field = BoundField(test_form, field_instance, "email") assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Primary") assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Secondary") # errors with pytest.raises(TypeError): crispy_addon() with pytest.raises(TypeError): crispy_addon(bound_field)
def uka_form_row_stacked(element, errors='', extra_classes=''): label = BoundField.label_tag(element, "", {'class': 'uk-form-label'}, label_suffix='') if errors: extra_classes = f'{extra_classes} uk-form-danger uk-clearfix' help_text = f'<div class="uk-text-muted uk-margin-small-top"><span uk-icon="icon: comment"></span> {element.help_text}</div>' \ if element.help_text else '' original_classes = element.field.widget.attrs.get('class', '') applied_classes = 'uk-margin-small-top uk-margin-small-bottom' if element.field.widget.__class__.__name__ == "Select": applied_classes = f'{original_classes} uk-select' elif element.field.widget.__class__.__name__ == "RelatedFieldWidgetWrapper": applied_classes = f'{original_classes} uk-select' elif element.field.widget.__class__.__name__ in ["AdminTextareaWidget", "TextareaWidget"]: applied_classes = f'{original_classes} uk-textarea' else: applied_classes = original_classes # Trying some overrides ''' if element.field.__class__.__name__ in ['SplitDateTimeField', 'ReadOnlyPasswordHashField', 'ModelMultipleChoiceField', 'QuillField']: element = element.as_widget() elif element.field.__class__.__name__ == 'TextareaWidget': element = element.as_widget(attrs={'class': f'{applied_classes}'}) else: ''' element = element.as_widget(attrs={'class': f'uk-input uk-form-width-large {applied_classes} {extra_classes}'}) html_error = format_html(f'<div class="uk-text-danger uk-margin-top">{errors}</div>') html = format_html(f'<div class="uk-form-row">' \ f' <div>{label} {html_error}</div>' \ f' <div class="uk-form-controls" style="clear: both">{element}{help_text}</div>' \ f'</div>') return html
def test_crispy_addon(settings): test_form = SampleForm() field_instance = test_form.fields["email"] bound_field = BoundField(test_form, field_instance, "email") if settings.CRISPY_TEMPLATE_PACK == "bootstrap": # prepend tests assert "input-prepend" in crispy_addon(bound_field, prepend="Work") assert "input-append" not in crispy_addon(bound_field, prepend="Work") # append tests assert "input-prepend" not in crispy_addon(bound_field, append="Primary") assert "input-append" in crispy_addon(bound_field, append="Secondary") # prepend and append tests assert "input-append" in crispy_addon(bound_field, prepend="Work", append="Primary") assert "input-prepend" in crispy_addon(bound_field, prepend="Work", append="Secondary") elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3": assert "input-group-addon" in crispy_addon(bound_field, prepend="Work", append="Primary") assert "input-group-addon" in crispy_addon(bound_field, prepend="Work", append="Secondary") elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4": assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Primary") assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Secondary") # errors with pytest.raises(TypeError): crispy_addon() with pytest.raises(TypeError): crispy_addon(bound_field)
def form_input_checkbox( form: forms.Form, field: BoundField, tooltip_src: str = "ui/svg/info-circle-fill.svg", placement: str = "right", ) -> str: """Form group with label, checkbox input, and tooltip.""" form_is_bound = form.is_bound errors = field.errors has_errors = form_is_bound and errors return format_html( """ <div class="form-group"> <div class="form-check"> <input type="checkbox" class="form-check-input {invalid_class}" id="{input_id}" {checked}/> <label class="form-check-label" for="{input_id}">{label}</label> <img id="{input_id}-tooltip" src="{static_url}{tooltip_src}" data-toggle="tooltip" data-placement="{placement}" title="{help_text}" /> </div> </div> """, input_id=field.id_for_label, label=field.label, invalid_class="is-invalid" if has_errors else "", checked="checked" if form_is_bound and field.value() else "", static_url=settings.STATIC_URL, tooltip_src=tooltip_src, placement=placement, help_text=field.help_text, )
def get_bound_field(self, form, field_name): """ Return a BoundField instance that will be used when accessing the form field in a template. """ return BoundField(form, self, field_name)
def bound_field_dict(self): return OrderedDict( (field_name, BoundField(self.form, field, field_name)) for (field_name, field) in self.form.fields.items() )
def _bound_value(self, field_name): field = self.fields[field_name] return BoundField(self, field, field_name).value()
def default(self, o, form=None, field_name=None): if issubclass(o.__class__, (forms.Form,forms.BaseForm)): flds = [] for name, field in o.fields.items(): if isinstance(field, dict): field['title'] = name else: field.name = name cfg = self.default(field, o, name) flds.append(cfg) return flds elif isinstance(o, dict): #Fieldset default_config = { 'autoHeight': True, 'collapsible': True, 'items': [], 'labelWidth': 200, 'title': o['title'], 'xtype':'fieldset', } del o['title'] #Ensure fields are added sorted by position for name, field in sorted(o.items()): field.name = name default_config['items'].append(self.default(field)) return default_config elif issubclass(o.__class__, fields.Field): #bf = form and form.is_bound and BoundField(form, o, field_name) or None bf = BoundField(form, o, field_name) #print field_name , o.__class__ default_config = {} if o.__class__ in self.DJANGO_EXT_FIELD_TYPES: default_config.update(self.DJANGO_EXT_FIELD_TYPES[o.__class__][1]) else: default_config.update(self.EXT_DEFAULT_CONFIG['editor']) config = deepcopy(default_config) if bf: config['invalidText']="".join(form[field_name].errors) if form and form.is_bound: data = bf.data else: if field_name: data = form.initial.get(field_name, o.initial) if callable(data): data = data() else: data = None config['value'] = data for dj, ext in self.DJANGO_EXT_FIELD_ATTRS.items(): v = None if dj == 'size': v = o.widget.attrs.get(dj, None) if v is not None: if o.__class__ in (fields.DateField, fields.DateTimeField, fields.SplitDateTimeField, fields.TimeField): v += 8 #Django's size attribute is the number of characters, #so multiply by the pixel width of a character v = v * self.CHAR_PIXEL_WIDTH elif dj == 'hidden': v = o.widget.attrs.get(dj, default_config.get('fieldHidden', ext[1])) elif dj == 'name': v = bf and bf.html_name or field_name elif dj == 'label': v = bf and bf.label or getattr(o, dj, ext[1]) elif getattr(o, dj, ext[1]) is None: #print "dj:%s field name:%s"%(dj,field_name) pass #elif dj == 'input_formats': #alt_fmts = [] ##Strip out the '%' placeholders #for fmt in getattr(field, dj, ext[1]): #alt_fmts.append(fmt.replace('%', '')) #v = u'|'.join(alt_fmts) elif isinstance(ext[1], basestring): v = getattr(o, dj, getattr(field, ext[1])) elif ext[0] == 'store': v = { 'autoLoad': True, 'storeId': o.name, 'url': '/csds/ext/rdo/queryset/%s/' % (o.name.lower(),), #'xtype': 'jsonstore', } elif dj == 'required': try: v = not getattr(o, dj) except AttributeError : v = ext[1] else: v = getattr(o, dj, ext[1]) if v is not None: if ext[0] == 'name': config[ext[0]] = v config['header'] = v elif ext[0] not in ('name', 'dataIndex', 'fieldLabel', 'header', 'defaultValue'): #elif ext[0] in ('allowBlank', 'listWidth', 'store', 'width'): #if isinstance(v, QuerySetIterator): # config['editor'][ext[0]] = list(v) config[ext[0]] = v if ext[0] == 'store': #config['url'] = v['url'] choices = [(c[0],c[1]) for c in o.choices] config['store'] = choices config['displayField'] = 'display' config['editable'] = False #config['editor']['forceSelection'] = True config['hiddenName'] = o.name #config['lastQuery'] = '' config['mode'] = 'local' config['triggerAction'] = 'all' #config['valueField'] = 'id' elif isinstance(v, unicode): config[ext[0]] = v.encode('utf8') else: config[ext[0]] = v return config else: return super(ExtJSONEncoder, self).default(o)
def __iter__(self): for name in self.fields: yield BoundField(self.form, self.form.fields[name], name)