11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Djangoで電話番号3つ区切りのフォームつくりたい!

え、Django標準で用意されてないの?
自作するのめんどくせー

たすけてー、GPTさまー!!!

勝ち

image.png

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つに分ける仕様はアンチパターンなのでは・・・?

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?