How do I write my own Django form widget?
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. Django Custom widget rendering
We can do like below.
Widget
from django import forms
from django.template import loader
from django.utils.safestring import mark_safe
class MyWidget(forms.Widget):
template_name = 'widget_template.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)
widget_template.html
<div class="form-group input-group">
<span class="input-group-addon">Name</span>
<input type="text" class="form-control" id="mywidget-{{ widget.name }}" name="{{ widget.name }}" />
</div>
Modify django model form field with widget
Method 1
The first method calls the constructor super().__init__(*args, **kwargs)
prior to manipulating the field. This enables developers to build the class in a default state and then play around with the classes components (attributes, functions).
The first method is most commonly used if the developer cannot achieve the results they want within the second method. This is because you're moving away from configuration to more of a manipulation of the class.
Method 2
The second method allows developers to define the configuration of the class before it is instantiated. This generally provides better usability and readability to other developers.
EXAMPLE:
Say you want your field bio
to be a required for all general users except for superusers.
class ProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop(user, None)
super().__init__(*args, **kwargs)
if self.user.is_superuser():
self.fields['bio'].required = False
class Meta:
model = Profile
fields = ['bio']
Using this method you can allow your Profile
model's bio
field attributes to be transposed to a form field upon instantiating and then making a small tweak to determine whether it's required for that particular user. This can be done without redefining the whole field.Note: The form call in the GET request would look like ProfileForm(user=request.user)
Related Topics
How to Use Numpy.Correlate to Do Autocorrelation
Numpy 1.21.2 May Not Yet Support Python 3.10
Counting Each Letter's Frequency in a String
Counting Cars Opencv + Python Issue
How to Get the Version Defined in Setup.Py (Setuptools) in My Package
Pandas Dataframe Column to List
Can You Make Multiple "If" Conditions in Python
Detect Tap with Pyaudio from Live Mic
Determine Prefix from a Set of (Similar) Strings
What Does 'Wb' Mean in This Code, Using Python
Adding a Y-Axis Label to Secondary Y-Axis in Matplotlib
How to Change Data Points Color Based on Some Variable
Why Does Numpy.Zeros Takes Up Little Space
Python and Operator on Two Boolean Lists - How
Python - Legend Overlaps with the Pie Chart
Python Argparse: Default Value or Specified Value
How to Upsert Pandas Dataframe to Microsoft SQL Server Table