Djangoで電話番号3つ区切りのフォームつくりたい!
↓
え、Django標準で用意されてないの?
自作するのめんどくせー
↓
たすけてー、GPTさまー!!!
勝ち
hogeapp/forms.py
from hogeapp.custom_forms import PhoneNumberField
class HogeForm(forms.Form):
phone_number = PhoneNumberField()
template.html
<div>
<label for="phone-number">{{ form.phone_number.label }}</label>
{{ form.phone_number }}
<div class="text-danger">
{{ form.phone_number.errors }}
</div>
</div>
hogeapp/lib/custom_forms.py
import re
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import MinLengthValidator, MaxLengthValidator
from django.forms import MultiValueField, MultiWidget
from django.forms.fields import CharField
from django.forms.widgets import TextInput
from django.utils.safestring import mark_safe
def validate_integer(value):
if not re.match("^[0-9]+$", value):
raise ValidationError('入力は整数でなければなりません。')
class PhoneNumberWidget(MultiWidget):
def __init__(self, attrs=None):
_attrs = attrs if attrs else {}
_attrs.update({"class": "form-control"})
widgets = [TextInput(attrs=_attrs), TextInput(attrs=_attrs), TextInput(attrs=_attrs)]
super().__init__(widgets, attrs)
def decompress(self, value):
if value:
return value.split('-')
return ['', '', '']
def render(self, name, value, attrs=None, renderer=None):
rendered_widgets = []
for index, widget_data in enumerate(self.get_context(name, value, attrs)['widget']['subwidgets']):
widget_instance = self.widgets[index]
rendered_widgets.append(widget_instance.render(name=widget_data['name'], value=widget_data['value'], attrs=widget_data['attrs']))
return mark_safe(f"<div style='display:flex; align-items: center;'>{rendered_widgets[0]}<span style='margin: 0 5px;'> - </span>{rendered_widgets[1]}<span style='margin: 0 5px;'> - </span>{rendered_widgets[2]}</div>")
class PhoneNumberField(MultiValueField):
widget = PhoneNumberWidget
def __init__(self, *args, **kwargs):
fields = (
CharField(validators=[validate_integer,
MinLengthValidator(2, '電話番号1(市外局番)は最低2桁です。'),
MaxLengthValidator(3, '電話番号1(市外局番)は最大3桁です。')]),
CharField(validators=[validate_integer,
MinLengthValidator(1, '電話番号2(市内局番)は最低1桁です。'),
MaxLengthValidator(4, '電話番号2(市内局番)は最大4桁です。')]),
CharField(validators=[validate_integer,
MinLengthValidator(4, '電話番号3(加入者番号)は4桁です。'),
MaxLengthValidator(4, '電話番号3(加入者番号)は4桁です。')]),
)
super().__init__(fields, *args, **kwargs)
def compress(self, data_list):
if data_list:
return '-'.join(data_list)
return ''
注意
- コピペして使ってバグっても責任を負いかねます🙇♂
- bootstrap5を使って開発している都合上、フォームにform-controlクラスをいれています
後述
そもそも電話番号フォームを3つに分ける仕様はアンチパターンなのでは・・・?