Skip to content

Commit

Permalink
Format local phone numbers as national when region is specified
Browse files Browse the repository at this point in the history
Display phone numbers in the most common form for users with a phone
number from the field region: the national format.

Co-authored-by: François Freitag <mail@franek.fr>
  • Loading branch information
fraimondo and francoisfreitag committed Oct 20, 2021
1 parent 84549a7 commit 2c0a5d4
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ UNRELEASED

- Add support for Python 3.10

**Backwards incompatible changes**

* ``formfields.PhoneNumberField`` with a ``region`` now display national phone
numbers in the national format instead of ``PHONENUMBER_DEFAULT_FORMAT``.
International numbers are displayed in the ``PHONENUMBER_DEFAULT_FORMAT``.

5.2.0 (2021-05-31)
------------------

Expand Down
20 changes: 19 additions & 1 deletion phonenumber_field/formfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _

from phonenumber_field.phonenumber import to_python, validate_region
from phonenumber_field.phonenumber import PhoneNumber, to_python, validate_region
from phonenumber_field.validators import validate_international_phonenumber


Expand Down Expand Up @@ -36,6 +36,24 @@ def __init__(self, *args, region=None, **kwargs):
error_message, example_number=example_number
)

def prepare_value(self, value):
if self.region and value not in validators.EMPTY_VALUES:
phone_number = (
value
if isinstance(value, PhoneNumber)
else to_python(value, region=self.region)
)
try:
phone_region_codes = phonenumbers.data._COUNTRY_CODE_TO_REGION_CODE[
phone_number.country_code
]
except KeyError:
pass
else:
if self.region in phone_region_codes:
value = phone_number.as_national
return value

def to_python(self, value):
phone_number = to_python(value, region=self.region)

Expand Down
8 changes: 7 additions & 1 deletion tests/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from phonenumber_field.formfields import PhoneNumberField

from .models import NullablePhoneNumber
from .models import NullablePhoneNumber, TestModelRegionAR


class PhoneNumberForm(forms.ModelForm):
Expand All @@ -13,3 +13,9 @@ class Meta:

class CustomPhoneNumberFormField(PhoneNumberField):
pass


class ARPhoneNumberForm(forms.ModelForm):
class Meta:
model = TestModelRegionAR
fields = ["phone"]
4 changes: 4 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@ class TestModelPhoneBEDNU(models.Model):

class FrenchPhoneOwner(models.Model):
cell_number = PhoneNumberField(region="FR")


class TestModelRegionAR(models.Model):
phone = PhoneNumberField(region="AR", blank=True, null=True)
118 changes: 117 additions & 1 deletion tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from phonenumber_field.phonenumber import PhoneNumber, to_python

from . import models
from .forms import CustomPhoneNumberFormField, PhoneNumberForm
from .forms import ARPhoneNumberForm, CustomPhoneNumberFormField, PhoneNumberForm

ALGERIAN_PHONE_NUMBER = "+213799136332"

Expand Down Expand Up @@ -519,3 +519,119 @@ def test_algerian_phone_number_in_model(self):
)
self.assertEqual(phonenumbers.parse(ALGERIAN_PHONE_NUMBER), m.phone_number)
self.assertEqual(ALGERIAN_PHONE_NUMBER, m.phone_number)

def test_region_field_empty(self):
obj = models.TestModelRegionAR.objects.create()
form = ARPhoneNumberForm(instance=obj)
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label>'
"<input "
'type="tel" '
'name="phone" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_international_as_E164(self):
form = ARPhoneNumberForm({"phone": "+32468547825"})
self.assertTrue(form.is_valid())
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label>'
"<input "
'type="tel" '
'name="phone" '
'value="+32468547825" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_international_as_E164_from_instance(self):
obj = models.TestModelRegionAR.objects.create(phone="+32468547825")
form = ARPhoneNumberForm(instance=obj)
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label> '
"<input "
'type="tel" '
'name="phone" '
'value="+32468547825" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_national_numbers_as_national(self):
form = ARPhoneNumberForm({"phone": "01145482368"})
self.assertTrue(form.is_valid())
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label>'
"<input "
'type="tel" '
'name="phone" '
'value="011 4548-2368" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_national_numbers_as_national_from_instance(self):
obj = models.TestModelRegionAR.objects.create(phone="01145482368")
form = ARPhoneNumberForm(instance=obj)
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label> '
"<input "
'type="tel" '
'name="phone" '
'value="011 4548-2368" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_national_numbers_from_instance_and_form_data(self):
obj = models.TestModelRegionAR.objects.create(phone="01145482368")
form = ARPhoneNumberForm({"phone": "011 4548-2368"}, instance=obj)
self.assertTrue(form.is_valid())
self.assertHTMLEqual(
form.as_p(),
"<p>"
'<label for="id_phone">Phone:</label> '
"<input "
'type="tel" '
'name="phone" '
'value="011 4548-2368" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

def test_region_field_renders_invalid_numbers(self):
form = ARPhoneNumberForm({"phone": "abcdef"})
self.assertFalse(form.is_valid())
self.assertHTMLEqual(
form.as_p(),
'<ul class="errorlist">'
"<li>Enter a valid phone number (e.g. 011 2345-6789) "
"or a number with an international call prefix.</li>"
"</ul>"
"<p>"
'<label for="id_phone">Phone:</label>'
"<input "
'type="tel" '
'name="phone" '
'value="abcdef" '
'maxlength="128" '
'id="id_phone">'
"</p>",
)

0 comments on commit 2c0a5d4

Please sign in to comment.