Django: How to create a custom form widget?

I find it difficult to find documentation on how to write a custom widget.

My questions:

  • If I create my own widget, can it be used equivalently for the admin interface or for normal forms?
  • If I want to allow the user to edit the list of elements, which widget should be a subclass? What widget methods do I need to override / implement?
  • What widget method is responsible for switching from user input back to the data model?

Thank.

+42
python django forms
Jan 16 '11 at 18:27
source share
5 answers

You are correct that Django does not provide documentation on this particular topic. I advise you to look at the built-in widgets in django.forms.widgets (I will refer to the classes from this module below).

If I create my own widget, can it be used equivalently for the admin interface or for normal forms?

The administrator overrides some widgets (see django.contrib.admin.options.FORMFIELD_FOR_DBFIELD_DEFAULTS ). Perhaps you can subclass ModelAdmin and change the formfield_overrides attribute, but I have never done anything with ModelAdmin , so I cannot help here ...

If I want to allow the user to edit the list of elements, which widget should be a subclass? What widget methods do I need to override / implement?

Your widget probably has nothing to do with the default widgets (with Select , if any ?!). A subclass from Widget , and if you find any common template with built-in, you can still change it later.

Follow these methods:

  • render(self, name, value, attrs=None)

    Check out Input.render for a simple example. It also supports custom attributes that are included in HTML. You can also add id attributes, see MultipleHiddenInput.render on how to do this. Remember to use mark_safe when outputting HTML directly. If you have a rather complicated widget, you can use template rendering ( example ).

  • _has_changed(self, initial, data)

    Additionally. Used by admin to log messages about what has been changed.

What widget method is responsible for switching from user input back to the data model?

This has nothing to do with widgets - Django cannot know which widget was used in an earlier request. It can only use form data (POST) submitted from the form. Therefore, the Field.to_python field method Field.to_python used to convert input to a Python data type ( ValidationError may be raised if the input is invalid).

+36
Jan 16 '11 at 10:20
source share

Django <1.11

In addition to the other answers, this is a small example of custom widget code:

widgets.py :

 from django.forms.widgets import Widget from django.template import loader from django.utils.safestring import mark_safe class MyWidget(Widget): template_name = 'myapp/my_widget.html' def get_context(self, name, value, attrs=None): return {'widget': { 'name': name, 'value': value, }} def render(self, name, value, attrs=None): context = self.get_context(name, value, attrs) template = loader.get_template(self.template_name).render(context) return mark_safe(template) 

my_widget.html :

 <textarea id="mywidget-{{ widget.name }}" name="{{ widget.name }}"> {% if widget.value %}{{ widget.value }}{% endif %}</textarea> 

Django 1.11

Widgets are now displayed using the form visualization API .

+13
Mar 02 '17 at 22:31 on
source share

NOTE. There are three questions here. For the first two questions, see AndiDog's more complete answer. I answer only the third question:

Q. What widget method is responsible for switching from user input back to the data model?

but. The value_from_datadict method is a kind of inverse widget render method. This method seems to apply to Django docs in widgets when it says: "The widget handles HTML rendering and retrieving data from the GET / POST dictionary corresponding to widgets." There is nothing in the documents at this point, but you can see how it works from the code of the embedded widgets.

+5
Jul 18 2018-12-18T00:
source share

I usually start by inheriting from one of the existing widgets, adding a new required property and then changing the rendering method. Here is an example for a filtered selection widget that I implemented. Filtering is done using jquery mobile.

 class FilterableSelectWidget(forms.Select): def __init__(self, attrs=None, choices=()): super(FilterableSelectWidget, self).__init__(attrs, choices) # choices can be any iterable, but we may need to render this widget # multiple times. Thus, collapse it into a list so it can be consumed # more than once. self._data_filter = {} @property def data_filter(self): return self._data_filter @data_filter.setter def data_filter(self, attr_dict): self._data_filter.update(attr_dict) def render_option(self, selected_choices, option_value, option_label): option_value = force_text(option_value) if option_value in selected_choices: selected_html = mark_safe(' selected="selected"') if not self.allow_multiple_selected: # Only allow for a single selection. selected_choices.remove(option_value) else: selected_html = '' # use self.data_filter filtertext = self.data_filter.get(option_value) data_filtertext = 'data-filtertext="{filtertext}"'.\ format(filtertext=filtertext) if filtertext else '' return format_html('<option value="{0}"{1} {3}>{2}</option>', option_value, selected_html, force_text(option_label), mark_safe(data_filtertext)) 

Then in the views where I create the form, I will set data_filter for the field.

  some_form.fields["some_field"] = \ forms.ChoiceField(choices=choices, widget=FilterableSelectWidget) some_form.fields["some_field"].widget.data_filter = \ data_filter 
+3
Dec 08 '14 at 22:20
source share

The documentation on the Django site does not help at all. These are suggestions for customizing widgets, here , abort the use of form.as_p() , which then compromises the meaning of the forms presented in Django, that is: building widgets.

The solutions I like best are floppyforms . This makes it easy to define widgets using templates and is an (almost) transparent replacement for the Django native forms module. It has excellent documentation and is easy to select.

0
Mar 10 '15 at 3:49
source share



All Articles