[Django]電話番号3つ区切りのフォームをつくった

Djangoで電話番号3つ区切りのフォームつくりたい！
↓
え、Django標準で用意されてないの？
自作するのめんどくせー
↓
たすけてー、GPTさまー！！！

# 勝ち
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493134/847865fd-fc90-633f-90f5-af44d4babcfa.png)

```python:hogeapp/forms.py
from hogeapp.custom_forms import PhoneNumberField

class HogeForm(forms.Form):
    phone_number = PhoneNumberField()
```
```html: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>
```

```python: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, '電話番号１(市外局番)は最低2桁です。'),
                                  MaxLengthValidator(3, '電話番号１(市外局番)は最大3桁です。')]),
            CharField(validators=[validate_integer,
                                  MinLengthValidator(1, '電話番号２（市内局番）は最低1桁です。'),
                                  MaxLengthValidator(4, '電話番号２（市内局番）は最大4桁です。')]),
            CharField(validators=[validate_integer,
                                  MinLengthValidator(4, '電話番号３（加入者番号）は4桁です。'),
                                  MaxLengthValidator(4, '電話番号３（加入者番号）は4桁です。')]),
        )
        super().__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            return '-'.join(data_list)
        return ''
```
## 注意
- コピペして使ってバグっても責任を負いかねます🙇‍♂
- bootstrap5を使って開発している都合上、フォームにform-controlクラスをいれています

# 後述
そもそも電話番号フォームを３つに分ける仕様はアンチパターンなのでは・・・？