You can look at the implementation of the date and time field, which displays 2 fields in the admin panel.
Top to bottom
admin uses
class AdminSplitDateTime(forms.SplitDateTimeWidget): """ A SplitDateTime Widget that has some admin-specific styling. """ def __init__(self, attrs=None): widgets = [AdminDateWidget, AdminTimeWidget]
which in turn uses SplitDateTimeWidget :
class SplitDateTimeWidget(MultiWidget): """ A Widget that splits datetime input into two <input type="text"> boxes. """ date_format = DateInput.format time_format = TimeInput.format def __init__(self, attrs=None, date_format=None, time_format=None): if date_format: self.date_format = date_format if time_format: self.time_format = time_format widgets = (DateInput(attrs=attrs, format=self.date_format), TimeInput(attrs=attrs, format=self.time_format)) super(SplitDateTimeWidget, self).__init__(widgets, attrs) def decompress(self, value): if value: return [value.date(), value.time().replace(microsecond=0)] return [None, None]
Which, in turn, extends the MultiWidget defined in django.forms.widgets , which you must also expand. It has many useful methods that you can override.
class MultiWidget(Widget): """ A widget that is composed of multiple widgets. Its render() method is different than other widgets', because it has to figure out how to split a single value for display in multiple widgets. The ``value`` argument can be one of two things: * A list. * A normal value (eg, a string) that has been "compressed" from a list of values. In the second case -- ie, if the value is NOT a list -- render() will first "decompress" the value into a list before rendering it. It does so by calling the decompress() method, which MultiWidget subclasses must implement. This method takes a single "compressed" value and returns a list. When render() does its HTML rendering, each value in the list is rendered with the corresponding widget -- the first value is rendered in the first widget, the second value is rendered in the second widget, etc. Subclasses may implement format_output(), which takes the list of rendered widgets and returns a string of HTML that formats them any way you'd like. You'll probably want to use this class with MultiValueField. """ def __init__(self, widgets, attrs=None): self.widgets = [isinstance(w, type) and w() or w for w in widgets] super(MultiWidget, self).__init__(attrs) def render(self, name, value, attrs=None):