def _render_field(self, field: BoundField, attrs: TypeAttrs = None) -> str: attrs = attrs or self._attrs_get_basic(self.attrs, field) placeholder = attrs.get('placeholder') if placeholder is None: if self.opt_placeholder_label: attrs['placeholder'] = field.label elif self.opt_placeholder_help: attrs['placeholder'] = field.help_text title = attrs.get('title') if title is None: title_label = self.opt_title_label title_help = self.opt_title_help if title_label or title_help: if title_label: attrs['title'] = field.label elif title_help: attrs['title'] = field.help_text out = field.as_widget(attrs=attrs) if field.field.show_hidden_initial: out += field.as_hidden(only_initial=True) return f'{out}'
def get_field_options(context: Context, field: BoundField) -> str: """ Retrieves the field options for a multiple choice field, and stores it in the context. This tag exists because we can't call functions within Django templates directly, and is only made use of in the template for ModelChoice (and derived) fields - but would work fine with anything that makes use of your standard `<select>` element widgets. This stores the parsed options under `options` in the context, which will subsequently be available in the template. Usage: ```django {% get_field_options field_object %} {% if options %} {% for group_name, group_choices, group_index in options %} ... {% endfor %} {% endif %} ``` """ widget = field.field.widget if field.value() is None: value: List[str] = [] else: value = [str(field.value())] context["options"] = widget.optgroups(field.name, value) return ""
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Override initial() to allow passing multiple values bound_field.initial = self._get_initial_value(form.initial, field_name) # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. data = bound_field.value() if data: filter = self.filter(field_name=self.to_field_name or 'pk', queryset=self.queryset) self.queryset = filter.filter(self.queryset, data) else: self.queryset = self.queryset.none() # Set the data URL on the APISelect widget (if not already set) widget = bound_field.field.widget if not widget.attrs.get('data-url'): app_label = self.queryset.model._meta.app_label model_name = self.queryset.model._meta.model_name data_url = reverse('{}-api:{}-list'.format(app_label, model_name)) widget.attrs['data-url'] = data_url return bound_field
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. data = bound_field.value() if data: field_name = getattr(self, 'to_field_name') or 'pk' filter = self.filter(field_name=field_name) try: self.queryset = filter.filter(self.queryset, data) except TypeError: # Catch any error caused by invalid initial data passed from the user self.queryset = self.queryset.none() else: self.queryset = self.queryset.none() # Set the data URL on the APISelect widget (if not already set) widget = bound_field.field.widget if not widget.attrs.get('data-url'): app_label = self.queryset.model._meta.app_label model_name = self.queryset.model._meta.model_name data_url = reverse('{}-api:{}-list'.format(app_label, model_name)) widget.attrs['data-url'] = data_url return bound_field
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): # Customized to handle special case for reCaptcha forms (not rendering remote_ip field) "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(): html_class_attr = '' bf = BoundField(self, field, name) bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. if not bf.is_hidden: # 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)) return mark_safe(u'\n'.join(output))
def test_get_field_options_value(self): unbound_field = ChoiceField() field = BoundField(Form(initial={"field": "Value"}), unbound_field, "field") context = Context({"field": field}) self.TEMPLATE.render(context)
def _convert_to_bound_fields(form, i18n_field_names): bound_fields = [] for field_name in i18n_field_names: local_fields = field_mapping[field_name] for local_name in local_fields: local_field = form_instance.fields[local_name] bound_field = BoundField(form, local_field, local_name) bound_fields.append(bound_field) return bound_fields
def _render_label(self, field: BoundField) -> str: label = field.label_tag( attrs=self._attrs_get_basic(self.attrs_labels, field), label_suffix=( # Get rid of colons entirely. '' if not self.opt_label_colon else ( # Or deduce... '' if isinstance(field.field.widget, CheckboxInput) else None))) return f'{label}'
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. data = self.prepare_value(bound_field.data or bound_field.initial) if data: filter = self.filter(field_name=self.to_field_name or 'pk', queryset=self.queryset) self.queryset = filter.filter(self.queryset, data) else: self.queryset = self.queryset.none() return bound_field
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. field_name = '{}{}'.format(self.to_field_name or 'pk', self.field_modifier) if bound_field.data: self.queryset = self.queryset.filter(**{field_name: self.prepare_value(bound_field.data)}) elif bound_field.initial: self.queryset = self.queryset.filter(**{field_name: self.prepare_value(bound_field.initial)}) else: self.queryset = self.queryset.none() return bound_field
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Set the initial value based on prescribed child fields (if not set) if not self.initial and self.initial_params: filter_kwargs = {} for kwarg, child_field in self.initial_params.items(): value = form.initial.get(child_field.lstrip("$")) if value: filter_kwargs[kwarg] = value if filter_kwargs: self.initial = self.queryset.filter(**filter_kwargs).first() # Modify the QuerySet of the field before we return it. Limit choices to any # data already bound: Options will be populated on-demand via the APISelect # widget data = bound_field.value() if data: field_name = getattr(self, "to_field_name") or "pk" filter = self.filter(field_name=field_name) try: self.queryset = filter.filter(self.queryset, data) except (TypeError, ValueError): # Catch errors caused by invalid initial data self.queryset = self.queryset.none() else: self.queryset = self.queryset.none() # Set the data URL on the APISelect widget (if not already set) widget = bound_field.field.widget if not widget.attrs.get("data-url"): data_url = reverse( f"{self.queryset.model._meta.app_label}-api:{self.queryset.model._meta.model_name}-list" ) widget.attrs["data-url"] = data_url return bound_field
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Set initial value based on prescribed child fields (if not already set) if not self.initial and self.initial_params: filter_kwargs = {} for kwarg, child_field in self.initial_params.items(): value = form.initial.get(child_field.lstrip('$')) if value: filter_kwargs[kwarg] = value if filter_kwargs: self.initial = self.queryset.filter(**filter_kwargs).first() # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. data = bound_field.value() if data: field_name = getattr(self, 'to_field_name') or 'pk' filter = self.filter(field_name=field_name) try: self.queryset = filter.filter(self.queryset, data) except TypeError: # Catch any error caused by invalid initial data passed from the user self.queryset = self.queryset.none() else: self.queryset = self.queryset.none() # Set the data URL on the APISelect widget (if not already set) widget = bound_field.field.widget if not widget.attrs.get('data-url'): app_label = self.queryset.model._meta.app_label model_name = self.queryset.model._meta.model_name data_url = reverse('{}-api:{}-list'.format(app_label, model_name)) widget.attrs['data-url'] = data_url return bound_field
def extract_widget_context(field: django_forms.BoundField) -> Dict[str, Any]: """ Previously we used a custom FormRenderer but there is *no way* to avoid the built in render method from piping this into `mark_safe`. So we monkeypatch the widget's internal renderer to return JSON directly without being wrapped by `mark_safe`. """ original_render = field.field.widget._render # type: ignore[union-attr] field.field.widget._render = ( # type: ignore[union-attr] lambda template_name, context, renderer: context) widget = field.as_widget() context: Any = widget["widget"] # type: ignore[index] context["template_name"] = getattr(field.field.widget, "reactivated_widget", context["template_name"]) # This is our first foray into properly serializing widgets using the # serialization framework. # # Eventually all widgets can be serialized this way and the frontend widget # types can disappear and be generated from the code here. # # We should not just handle optgroups but every property, and do so # recursively. def handle_optgroups(widget_context: Any) -> None: optgroups = widget_context.get("optgroups", None) if optgroups is not None: optgroup_schema = create_schema(Optgroup, {}) widget_context["optgroups"] = [ serialize(optgroup, optgroup_schema) for optgroup in optgroups ] for subwidget_context in context.get("subwidgets", []): handle_optgroups(subwidget_context) handle_optgroups(context) field.field.widget._render = original_render # type: ignore[union-attr] return context # type: ignore[no-any-return]
def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) # Modify the QuerySet of the field before we return it. Limit choices to any # data already bound: Options will be populated on-demand via the APISelect # widget data = self.prepare_value(bound_field.data or bound_field.initial) if data: filter = self.filter(field_name=self.to_field_name or "pk", queryset=self.queryset) self.queryset = filter.filter(self.queryset, data) else: self.queryset = self.queryset.none() # Set the data URL on the APISelect widget (if not already set) widget = bound_field.field.widget if not widget.attrs.get("data-url"): data_url = reverse( f"{self.queryset.model._meta.app_label}-api:{self.queryset.model._meta.model_name}-list" ) widget.attrs["data-url"] = data_url return bound_field
def test_bound_field(self): unbound_field = Field() field = BoundField(Form(), unbound_field, "field") context = Context({"field": field}) self.TEMPLATE.render(context)
def run_field_checks(self, rendered_field: BoundField): if rendered_field.widget_type == 'checkbox': # Set custom attribute for use in the template rendered_field.is_checkbox = True if isinstance(rendered_field.field.widget, FileInput): self._has_file_field = True
def test_bound_field_labels_not_boolean(self): unbound_field = Field() field = BoundField(Form(), unbound_field, "field") context = Context({"field": field}) self.TEMPLATE_LABELS_NOT_BOOLEAN.render(context)
def test_bound_field_no_labels(self): unbound_field = Field() field = BoundField(Form(), unbound_field, "field") context = Context({"field": field}) self.TEMPLATE_NO_LABELS.render(context)
def _html_output(self, 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 fieldset, fields in self.fieldsets: if fieldset: output.append( normal_row % { 'errors': '', 'label': ' ', 'field': self.fieldset_template % fieldset, 'help_text': '' }) for name, field in [ i for i in self.fields.items() if i[0] in fields ]: bf = BoundField(self, field, name) bf_errors = self.error_class([ 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: if errors_on_separate_row and bf_errors: output.append(error_row % force_unicode(bf_errors)) if bf.label: label = 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 }) 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. 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 jet_select2_lookups(field: BoundField): form_field: Field = getattr(field, 'field', None) if not (form_field and (isinstance(form_field, ModelChoiceField) or isinstance(form_field, ModelMultipleChoiceField))): return field qs = form_field.queryset model = qs.model if not (getattr(model, 'autocomplete_search_fields', None) and getattr(form_field, 'autocomplete', True)): return field choices = [] app_label = model._meta.app_label model_name = model._meta.object_name url = getattr(form_field, 'url', reverse('jet:model_lookup')) data = getattr(form_field.widget, 'data', {}) data['blank'] = not form_field.required attrs = { 'class': 'ajax', 'data-app-label': app_label, 'data-model': model_name, 'data-ajax--url': url, **format_widget_data(data), } initial_value = field.value() if isinstance(form_field, ModelMultipleChoiceField): if initial_value: initial_objects = model.objects.filter(pk__in=initial_value) choices.extend([(initial_object.pk, get_model_instance_label(initial_object)) for initial_object in initial_objects]) if isinstance(form_field.widget, RelatedFieldWidgetWrapper): form_field.widget.widget = SelectMultiple(attrs) else: form_field.widget = SelectMultiple(attrs) form_field.choices = choices elif isinstance(form_field, ModelChoiceField): if initial_value: try: initial_object = model.objects.get(pk=initial_value) attrs['data-object-id'] = initial_value choices.append((initial_object.pk, get_model_instance_label(initial_object))) except model.DoesNotExist: pass if isinstance(form_field.widget, RelatedFieldWidgetWrapper): form_field.widget.widget = Select(attrs) else: form_field.widget = Select(attrs) form_field.choices = choices return field
def class_attr(text: BoundField, args: str) -> BoundField: css_cls = ' '.join([c.strip() for c in args.split(',')]) return text.as_widget(attrs={'class':'{}'.format(css_cls)})
def form_filed(field: BoundField): # type(filed) field.css_classes('layui-input') # field.build_widget_attrs({'class': "layui-input", }) return mark_safe(field)
def test_get_field_options(self): unbound_field = ChoiceField() field = BoundField(Form(), unbound_field, "field") context = Context({"field": field}) self.TEMPLATE.render(context)