0

Could anyone provide a simple example, so that I understand the basic idioms of how this is done? I can't find any helpful documentation that I can understand on the topic.

To provide a little more context, I'd like to create my own radio buttons, that are rendered in a particular way.

I'm just looking for a very simple example to help me understand this concept.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Jarrod
  • 1,655
  • 2
  • 21
  • 35
  • There is a comment in that question pointing to: http://tothinkornottothink.com/post/10815277049/django-forms-i-custom-fields-and-widgets-in – Nagaraj Tantri Jul 03 '16 at 06:46

1 Answers1

0

It's hard to tell what you mean by "a very simple example". Here is a very simple example:

from django.forms.widgets import Input

class TelInput(Input):
    input_type = 'tel'

But I don't think this will help you much.

If you are looking for examples, best is still to check out django source code.

I think this should be sufficient to understand how it works:

from django.utils.encoding import force_text
from django.utils.html import format_html
from django.forms.utils import flatatt

class Widget(...):

    def __init__(self, attrs=None):
        if attrs is not None:
            self.attrs = attrs.copy()
        else:
            self.attrs = {}

    def subwidgets(self, name, value, attrs=None, choices=()):
        """
        Yields all "subwidgets" of this widget. Used only by RadioSelect to
        allow template access to individual <input type="radio"> buttons.
        Arguments are the same as for render().
        """
        yield SubWidget(self, name, value, attrs, choices)

    def render(self, name, value, attrs=None):
        """
        Returns this Widget rendered as HTML, as a Unicode string.
        The 'value' given is not guaranteed to be valid input, so subclass
        implementations should program defensively.
        """
        raise NotImplementedError('subclasses of Widget must provide a render() method')

    def build_attrs(self, extra_attrs=None, **kwargs):
        "Helper function for building an attribute dictionary."
        attrs = dict(self.attrs, **kwargs)
        if extra_attrs:
            attrs.update(extra_attrs)
        return attrs

    def value_from_datadict(self, data, files, name):
        """
        Given a dictionary of data and this widget's name, returns the value
        of this widget. Returns None if it's not provided.
        """
        return data.get(name)


class Input(Widget):
    """
    Base class for all <input> widgets (except type='checkbox' and
    type='radio', which are special).
    """
    input_type = None  # Subclasses must define this.

    def format_value(self, value):
        if self.is_localized:
            return formats.localize_input(value)
        return value

    def render(self, name, value, attrs=None):
        if value is None:
            value = ''
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
        if value != '':
            # Only add the 'value' attribute if a value is non-empty.
            final_attrs['value'] = force_text(self.format_value(value))
        return format_html('<input{} />', flatatt(final_attrs))

Most relevant methods are render() that renders the widget as HTML code, and value_from_datadict() that extracts the value of the widget from the POST data dictionary.

Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
  • Note that widgets' HTML MUST have a name attribute, or else the Django admin will not be able to read their values, and they will just be silently excluded from the form submission. – Alan Porter Aug 05 '20 at 16:41
  • @AlanPorter This is handled by `final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)`. The name is determined by the form, not the widget itself. Behind the scene, Django passes the field name to the `render()` method. – Antoine Pinsard Aug 06 '20 at 09:53
  • Ah, yes... I did not mean to imply that this example suffered from that missing name. I was working from another example found on a blog, and I spent two days debugging it. I just wanted anyone following the same trail I followed to be careful to include the name. Thanks, @Antoine-Pinsard ! – Alan Porter Aug 07 '20 at 14:21