| 1 | '''
|
|---|
| 2 | my_project/my_widgets.py
|
|---|
| 3 |
|
|---|
| 4 | '''
|
|---|
| 5 | from django.forms import CheckboxInput, MultiWidget
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 | class NullableWidget(MultiWidget):
|
|---|
| 9 | """
|
|---|
| 10 | A Widget that wraps another Widget and renders it along with a checkbox
|
|---|
| 11 | that unchecked means that the value should be None (NULL) [#4136]
|
|---|
| 12 |
|
|---|
| 13 | """
|
|---|
| 14 | def __init__(self, subwidget, attrs=None):
|
|---|
| 15 | self.checkbox = CheckboxInput()
|
|---|
| 16 | self.subwidget = subwidget
|
|---|
| 17 | super(NullableWidget, self).__init__(
|
|---|
| 18 | [self.checkbox, self.subwidget], attrs
|
|---|
| 19 | )
|
|---|
| 20 |
|
|---|
| 21 | def decompress(self, value):
|
|---|
| 22 | if value is None:
|
|---|
| 23 | return [False, '']
|
|---|
| 24 | else:
|
|---|
| 25 | return [True, value]
|
|---|
| 26 |
|
|---|
| 27 | def value_from_datadict(self, data, files, name):
|
|---|
| 28 | is_set, value = [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
|
|---|
| 29 |
|
|---|
| 30 | if is_set:
|
|---|
| 31 | return value
|
|---|
| 32 | else:
|
|---|
| 33 | return None
|
|---|
| 34 |
|
|---|
| 35 | def _has_changed(self, initial, data):
|
|---|
| 36 | '''
|
|---|
| 37 | When checkbox is unticked and Text
|
|---|
| 38 | '''
|
|---|
| 39 | if initial is None:
|
|---|
| 40 | initial = self.decompress(initial)
|
|---|
| 41 | else:
|
|---|
| 42 | if not isinstance(initial, list):
|
|---|
| 43 | initial = self.decompress(initial)
|
|---|
| 44 |
|
|---|
| 45 | multi_data = self.decompress(data)
|
|---|
| 46 | for widget, initial, data in zip(self.widgets, initial, multi_data):
|
|---|
| 47 | if widget._has_changed(initial, data):
|
|---|
| 48 | return True
|
|---|
| 49 | return False
|
|---|
| 50 |
|
|---|
| 51 | '''
|
|---|
| 52 | my_project/models.py
|
|---|
| 53 |
|
|---|
| 54 | '''
|
|---|
| 55 | from django.db import models
|
|---|
| 56 |
|
|---|
| 57 | class MyModel(models.Model):
|
|---|
| 58 | my_nullable_charfield = models.CharField("verbose name",
|
|---|
| 59 | blank=True,
|
|---|
| 60 | null=True,
|
|---|
| 61 | unique=True,
|
|---|
| 62 | default=None,
|
|---|
| 63 | max_length=3)
|
|---|
| 64 | '''
|
|---|
| 65 | my_project/forms.py
|
|---|
| 66 |
|
|---|
| 67 | '''
|
|---|
| 68 | from django import forms
|
|---|
| 69 |
|
|---|
| 70 | from my_project.models import MyModel
|
|---|
| 71 | from my_project.my_widgets import NullableWidget
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 | class MyModelForm(forms.ModelForm):
|
|---|
| 75 | class Meta:
|
|---|
| 76 | model = MyModel
|
|---|
| 77 | widgets = {
|
|---|
| 78 | "my_nullable_charfield": NullableWidget(forms.TextInput())
|
|---|
| 79 | }
|
|---|