Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions phonenumber_field/formfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,16 @@ class SplitPhoneNumberField(MultiValueField):
default_validators = [validate_international_phonenumber]
widget = widgets.PhoneNumberPrefixWidget

def __init__(self, *, initial=None, region=None, widget=None, **kwargs):
def __init__(
self,
*,
initial=None,
region=None,
widget=None,
empty_value="",
max_length=None,
**kwargs,
):
"""
:keyword list initial: A two-elements iterable:

Expand All @@ -122,6 +131,9 @@ def __init__(self, *, initial=None, region=None, widget=None, **kwargs):
When not supplied, defaults to :setting:`PHONENUMBER_DEFAULT_REGION`
:keyword ~django.forms.MultiWidget widget: defaults to
:class:`~phonenumber_field.widgets.PhoneNumberPrefixWidget`
:keyword empty_value: value to use when the field is empty
:keyword int max_length: maximum length of the phone number, when
represented as :setting:`PHONENUMBER_DB_FORMAT`.
"""
validate_region(region)
region = region or getattr(settings, "PHONENUMBER_DEFAULT_REGION", None)
Expand All @@ -132,6 +144,8 @@ def __init__(self, *, initial=None, region=None, widget=None, **kwargs):
fields = (prefix_field, number_field)
if widget is None:
widget = self.widget((prefix_field.widget, number_field.widget))
self.empty_value = empty_value
self.max_length = max_length
super().__init__(fields, initial=initial, widget=widget, **kwargs)

def prefix_field(self):
Expand Down Expand Up @@ -168,7 +182,7 @@ def invalid_error_message(self):

def compress(self, data_list):
if not data_list:
return data_list
return self.empty_value
region, national_number = data_list
return to_python(national_number, region=region)

Expand All @@ -188,4 +202,19 @@ def clean(self, value):
error_message,
example_number=example_number,
)
return super().clean(value)
clean_value = super().clean(value)
if self.max_length is not None:
phonenumber_str = clean_value.format_as(
getattr(settings, "PHONENUMBER_DB_FORMAT", "E164")
)
if len(phonenumber_str) > self.max_length:
raise ValidationError(
format_lazy(
"Ensure this value has no more than {max_length} characters, "
"“{value}” is {length} characters long.",
max_length=self.max_length,
value=phonenumber_str,
length=len(phonenumber_str),
)
)
return clean_value
4 changes: 4 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,7 @@ class FrenchPhoneOwner(models.Model):

class TestModelRegionAR(models.Model):
phone = PhoneNumberField(region="AR", blank=True, null=True)


class PhoneNumberWithMaxLength(models.Model):
phone = PhoneNumberField(max_length=3)
49 changes: 49 additions & 0 deletions tests/test_formfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from phonenumber_field.formfields import PhoneNumberField, SplitPhoneNumberField
from phonenumber_field.phonenumber import PhoneNumber
from phonenumber_field.validators import validate_phonenumber
from tests.models import NullablePhoneNumber, PhoneNumberWithMaxLength

ALGERIAN_PHONE_NUMBER = "+213799136332"

Expand Down Expand Up @@ -317,6 +318,7 @@ class TestForm(forms.Form):

form = TestForm(data={"phone_0": "", "phone_1": ""})
self.assertIs(form.is_valid(), True)
self.assertEqual(form.cleaned_data["phone"], "")

def test_no_region(self):
class TestForm(forms.Form):
Expand Down Expand Up @@ -389,6 +391,17 @@ class TestForm(forms.Form):

form = TestForm(data={})
self.assertIs(form.is_valid(), True)
self.assertEqual(form.cleaned_data["phone"], "")

def test_not_required_empty_value(self):
class TestForm(forms.Form):
phone = SplitPhoneNumberField(required=False)
phone_none = SplitPhoneNumberField(required=False, empty_value=None)

form = TestForm(data={})
self.assertIs(form.is_valid(), True)
self.assertEqual(form.cleaned_data["phone"], "")
self.assertIsNone(form.cleaned_data["phone_none"])

def test_keeps_region_with_invalid_national_number(self):
class TestForm(forms.Form):
Expand Down Expand Up @@ -696,3 +709,39 @@ class TestForm(forms.Form):

form = TestForm({"phone_0": "FR", "phone_1": "1010"})
self.assertIs(form.is_valid(), True)

def test_formfield_with_maxlength_null(self):
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields = forms.fields_for_model(
NullablePhoneNumber,
["phone_number"],
field_classes={"phone_number": SplitPhoneNumberField},
)

form = TestForm(data={"phone_number_0": "FR", "phone_number_1": "612345678"})
self.assertIs(form.is_valid(), True)
self.assertEqual(form.cleaned_data["phone_number"], "+33612345678")

def test_formfield_with_max_length(self):
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields = forms.fields_for_model(
PhoneNumberWithMaxLength,
["phone"],
field_classes={"phone": SplitPhoneNumberField},
)

form = TestForm(data={"phone_0": "FR", "phone_1": "33612345678"})
self.assertIs(form.is_valid(), False)
self.assertEqual(
form.errors,
{
"phone": [
"Ensure this value has no more than 3 characters, "
"“6 12 34 56 78” is 13 characters long."
],
},
)
Loading