Back to Django Phonenumber Field

The PhoneNumber wrapper

docs/reference.rst

8.4.016.6 KB
Original Source

.. testsetup:: *

import django from bs4 import BeautifulSoup

django.setup()

def print_html(html): print(BeautifulSoup(html).prettify().strip())

def print_html_compact(html): output = BeautifulSoup(html).prettify().strip().splitlines() print("\n".join(output[:15])) print(" ...") print("\n".join(output[-7:]))

The PhoneNumber wrapper

.. autoclass:: phonenumber_field.phonenumber.PhoneNumber()

.. automethod:: phonenumber_field.phonenumber.PhoneNumber.from_string

.. automethod:: phonenumber_field.phonenumber.PhoneNumber.is_valid

.. autoproperty:: phonenumber_field.phonenumber.PhoneNumber.as_international

.. autoproperty:: phonenumber_field.phonenumber.PhoneNumber.as_national

.. autoproperty:: phonenumber_field.phonenumber.PhoneNumber.as_e164

.. autoproperty:: phonenumber_field.phonenumber.PhoneNumber.as_rfc3966

Usage

.. doctest:: wrapper

from phonenumber_field.phonenumber import PhoneNumber

number = PhoneNumber.from_string("+16044011234") print(number.as_national) (604) 401-1234 print(number.as_e164) +16044011234 print(number.as_international) +1 604-401-1234 print(number.as_rfc3966) tel:+1-604-401-1234

Using national numbers with the region keyword argument.

canadian_number = "(604) 401 1234" number = PhoneNumber.from_string(canadian_number, region="CA") print(number.as_e164) +16044011234

Model field

The :class:~phonenumber_field.modelfields.PhoneNumberField :ref:model field <topics/db/models:fields> allows storing :class:~phonenumber_field.phonenumber.PhoneNumber\ s in the database, based on a :class:~django.db.models.CharField.

The phone number format used by the database is controlled with :setting:PHONENUMBER_DB_FORMAT. When no region is specified, a phone number in the "NATIONAL" format will be assumed to be from the :setting:PHONENUMBER_DEFAULT_REGION.

  • The default :ref:form field <ref/forms/fields:form fields> for this field is the :class:~phonenumber_field.formfields.PhoneNumberField.
  • The default :ref:form widget <ref/forms/widgets:widgets> for this field is the :class:~phonenumber_field.widgets.RegionalPhoneNumberWidget.

.. autoclass:: phonenumber_field.modelfields.PhoneNumberField

.. automethod:: init

Usage

.. code-block:: python

from django.db import models from phonenumber_field.modelfields import PhoneNumberField

class Customer(models.Model): name = models.TextField() # An optional phone number. phone_number = PhoneNumberField(blank=True)

Form fields

:class:~phonenumber_field.formfields.PhoneNumberField

The :class:~phonenumber_field.formfields.PhoneNumberField :ref:form field <ref/forms/fields:form fields> to validate :class:~phonenumber_field.phonenumber.PhoneNumber, based on a :class:~django.forms.CharField.

  • Default widget: :class:~phonenumber_field.widgets.RegionalPhoneNumberWidget
  • Empty value: ""
  • Normalizes to: A :class:~phonenumber_field.phonenumber.PhoneNumber or an empty :class:str ("")
  • Uses :func:~phonenumber_field.validators.validate_international_phonenumber

.. autoclass:: phonenumber_field.formfields.PhoneNumberField

.. automethod:: init

Usage


.. doctest:: formfield

   >>> from phonenumber_field.formfields import PhoneNumberField

   >>> class PhoneForm(django.forms.Form):
   ...     number = PhoneNumberField(region="CA")
   ...

   # Manipulating data
   >>> form = PhoneForm({"number": "+1 604 401 1234"})
   >>> form.is_valid()
   True
   >>> form.cleaned_data
   {'number': PhoneNumber(country_code=1, national_number=6044011234, extension=None, italian_leading_zero=None, number_of_leading_zeros=None, country_code_source=1, preferred_domestic_carrier_code=None)}
   >>> print_html(form.as_div())
   <div>
    <label for="id_number">
     Number:
    </label>
    <input id="id_number" name="number" required="" type="tel" value="(604) 401-1234"/>
   </div>

   # Handling errors
   >>> form = PhoneForm({"number": "invalid"})
   >>> form.is_valid()
   False
   >>> print_html(form.as_div())
   <div>
    <label for="id_number">
     Number:
    </label>
    <ul class="errorlist" id="id_number_error">
     <li>
      Enter a valid phone number (e.g. (506) 234-5678) or a number with an international call prefix.
     </li>
    </ul>
    <input aria-describedby="id_number_error" aria-invalid="true" id="id_number" name="number" required="" type="tel" value="invalid"/>
   </div>

.. note:: Because the PhoneNumberField specifies a region, the example number
   is a national number from that region. When no region is specified, an
   international example phone number in the E.164 format is suggested.


:class:`~phonenumber_field.formfields.SplitPhoneNumberField`
------------------------------------------------------------

A :class:`~django.forms.MultiValueField` that offers:

- a ``<select … >`` element to choose the region, and
- an ``<input type="tel" … >`` to enter the phone number.

To customize each field, subclass
:class:`~phonenumber_field.formfields.SplitPhoneNumberField` and override:

- :func:`~phonenumber_field.formfields.SplitPhoneNumberField.prefix_field`
  for the phone number prefix field,
- :func:`~phonenumber_field.formfields.SplitPhoneNumberField.number_field`
  for the phone number field.

This widget uses an example phone number from the selected region for the
``invalid`` key in :attr:`~django.forms.Field.error_messages`, when the region
choice is valid.

To customize the dynamic message, use
:func:`phonenumber_field.formfields.SplitPhoneNumberField.invalid_error_message`.

.. important:: Requires the `Babel <https://pypi.org/project/Babel/>`_ package.

.. autoclass:: phonenumber_field.formfields.SplitPhoneNumberField

   .. automethod:: __init__
   .. automethod:: prefix_field
   .. automethod:: number_field
   .. automethod:: invalid_error_message

Usage

Simple ......

.. doctest:: SplitPhoneNumberField.basic

from phonenumber_field.formfields import SplitPhoneNumberField

class PhoneForm(django.forms.Form): ... number = SplitPhoneNumberField() ...

form = PhoneForm() print_html_compact(form.as_div())

<div> <fieldset> <legend> Number: </legend> <select id="id_number_0" name="number_0" required=""> <option selected="" value=""> --------- </option> <option value="AF"> Afghanistan +93 </option> <option value="AL"> Albania +355 </option> ... <option value="AX"> Åland Islands +358 </option> </select> <input id="id_number_1" name="number_1" required="" type="tel"/> </fieldset> </div>

Limiting country choices ........................

.. doctest:: SplitPhoneNumberField.country_choices

from phonenumber_field.formfields import SplitPhoneNumberField

class DemoSplitPhoneNumberField(SplitPhoneNumberField): ... def prefix_field(self): ... return django.forms.ChoiceField(choices=[ ... ("", "---------"), ... ("CA", "Canada"), ... ("FR", "France"), ... ]) ... class LimitedCountryPhoneForm(django.forms.Form): ... number = DemoSplitPhoneNumberField() ... form = LimitedCountryPhoneForm() print_html(form.as_div())

<div> <fieldset> <legend> Number: </legend> <select id="id_number_0" name="number_0" required=""> <option selected="" value=""> --------- </option> <option value="CA"> Canada </option> <option value="FR"> France </option> </select> <input id="id_number_1" name="number_1" required="" type="tel"/> </fieldset> </div>

Pre-selecting a country .......................

.. doctest:: SplitPhoneNumberField.preselecting_country

from phonenumber_field.formfields import SplitPhoneNumberField

class DemoSplitPhoneNumberField(SplitPhoneNumberField): ... def prefix_field(self): ... return django.forms.ChoiceField(choices=[ ... ("", "---------"), ... ("CA", "Canada"), ... ("FR", "France"), ... ]) ...

class FrenchPhoneForm(django.forms.Form): ... number = DemoSplitPhoneNumberField(region="FR") ...

form = FrenchPhoneForm() print_html(form.as_div())

<div> <fieldset> <legend> Number: </legend> <select id="id_number_0" name="number_0" required=""> <option value=""> --------- </option> <option value="CA"> Canada </option> <option selected="" value="FR"> France </option> </select> <input id="id_number_1" name="number_1" required="" type="tel"/> </fieldset> </div>

Customizing widget attrs ........................

.. doctest:: SplitPhoneNumberField.preselecting_country

from phonenumber_field.formfields import SplitPhoneNumberField

class DemoSplitPhoneNumberField(SplitPhoneNumberField): ... def prefix_field(self): ... return django.forms.ChoiceField(choices=[ ... ("", "---------"), ... ("CA", "Canada"), ... ("FR", "France"), ... ]) ... ... def number_field(self): ... number_field = super().number_field() ... number_field.widget.attrs["class"] = "form-control" ... return number_field ...

class BootstrapPhoneForm(django.forms.Form): ... number = DemoSplitPhoneNumberField() ...

form = BootstrapPhoneForm() print_html(form.as_div())

<div> <fieldset> <legend> Number: </legend> <select id="id_number_0" name="number_0" required=""> <option selected="" value=""> --------- </option> <option value="CA"> Canada </option> <option value="FR"> France </option> </select> <input class="form-control" id="id_number_1" name="number_1" required="" type="tel"/> </fieldset> </div>

Widgets

RegionalPhoneNumberWidget


**Default widget** for :class:`~phonenumber_field.formfields.PhoneNumberField`.

- input_type: ``tel``
- Renders as ``<input type="tel" … >``

.. important:: The region should be specified (either per field using the
   ``region`` keyword argument, or with the
   :setting:`PHONENUMBER_DEFAULT_REGION` setting) in order to know which
   national number format to recognize.


.. autoclass:: phonenumber_field.widgets.RegionalPhoneNumberWidget

   .. automethod:: __init__

Usage
.....

.. doctest:: fallbackwidget

   >>> from phonenumber_field.formfields import PhoneNumberField

   >>> class CanadianPhoneForm(django.forms.Form):
   ...     # RegionalPhoneNumberWidget is the default widget.
   ...     number = PhoneNumberField(region="CA")
   ...

   # Using the national format for the field’s region.
   >>> form = CanadianPhoneForm({"number": "+16044011234"})
   >>> print_html(form.as_div())
   <div>
    <label for="id_number">
     Number:
    </label>
    <input id="id_number" name="number" required="" type="tel" value="(604) 401-1234"/>
   </div>

   # Using E164 for an international number.
   >>> french_number = "+33612345678"
   >>> form = CanadianPhoneForm({"number": french_number})
   >>> print_html(form.as_div())
   <div>
    <label for="id_number">
     Number:
    </label>
    <input id="id_number" name="number" required="" type="tel" value="+33612345678"/>
   </div>

PhoneNumberPrefixWidget
~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: phonenumber_field.widgets.PhoneNumberPrefixWidget

Serializer field
================

The :class:`~phonenumber_field.serializersfields.PhoneNumberField` `serializer
field <https://www.django-rest-framework.org/api-guide/fields/>`_, based on the
`CharField
<https://www.django-rest-framework.org/api-guide/fields/#charfield>`_.

The serialization format is controlled by the
:setting:`PHONENUMBER_DEFAULT_FORMAT`.

.. autoclass:: phonenumber_field.serializerfields.PhoneNumberField

   .. automethod:: __init__

Usage
-----

.. doctest:: serializerfield

    >>> from django.conf import settings
    >>> from rest_framework import renderers, serializers
    >>> from phonenumber_field.serializerfields import PhoneNumberField

    >>> class PhoneNumberSerializer(serializers.Serializer):
    ...     number = PhoneNumberField(region="CA")
    ...

    >>> serializer = PhoneNumberSerializer(data={"number": "604 401 1234"})
    >>> serializer.is_valid()
    True
    >>> serializer.validated_data
    {'number': PhoneNumber(country_code=1, national_number=6044011234, extension=None, italian_leading_zero=None, number_of_leading_zeros=None, country_code_source=20, preferred_domestic_carrier_code=None)}

    # Using the PHONENUMBER_DEFAULT_FORMAT.
    >>> renderers.JSONRenderer().render(serializer.data)
    b'{"number":"+16044011234"}'


Validator
=========

Validates:

- a :class:`~phonenumber_field.phonenumber.PhoneNumber` instance, or
- an :class:`str` in an international format (with a prefix), or
- an :class:`str` that’s a local phone number according to
  :setting:`PHONENUMBER_DEFAULT_REGION`.

.. note:: Not all well-formed phone numbers are valid. The rules to construct
   phone numbers vary per region of the world.

   `Falsehoods Programmers Believe About Phone Numbers
   <https://github.com/google/libphonenumber/blob/master/FALSEHOODS.md>`_ is a
   good read.

.. autofunction:: phonenumber_field.validators.validate_international_phonenumber

**code**: ``"invalid"``

Settings
========

.. _settings-format-choices:

Phone number format choices
---------------------------

+------------------------+---------------+------------+-----------------------------------------------------------------+
| Setting value          | International | Extensions | Notes                                                           |
+========================+===============+============+=================================================================+
| ``"E164"`` *(default)* | ✓             | ❌         | https://en.wikipedia.org/wiki/E.164                             |
+------------------------+---------------+------------+-----------------------------------------------------------------+
| ``"INTERNATIONAL"``    | ✓             | ✓          | https://en.wikipedia.org/wiki/E.123#Telephone_number            |
+------------------------+---------------+------------+-----------------------------------------------------------------+
| ``"RFC3966"``          | ✓             | ✓          | https://www.rfc-editor.org/rfc/rfc3966.html                     |
+------------------------+---------------+------------+-----------------------------------------------------------------+
| ``"NATIONAL"``         | ❌            | ✓          | **DISCOURAGED**, requires :setting:`PHONENUMBER_DEFAULT_REGION` |
+------------------------+---------------+------------+-----------------------------------------------------------------+

.. warning::

    By default, the library uses `E.164, the international public
    telecommunication numbering plan <https://en.wikipedia.org/wiki/E.164>`_,
    which **does not support phone numbers extensions**. Set **both**
    :setting:`PHONENUMBER_DB_FORMAT` and :setting:`PHONENUMBER_DEFAULT_FORMAT`
    to an extension-compatible format to handle phone numbers extensions.

.. setting:: PHONENUMBER_DB_FORMAT

``PHONENUMBER_DB_FORMAT``
-------------------------

Store phone numbers strings in the specified format in the database.

Default: ``"E164"``.

See :ref:`settings-format-choices`.

.. warning:: **Data loss may occur when changing the DB format.**

   Phone numbers stored in the database are parsed every time they are loaded
   from the database.

   When switching from a format that can represent extensions to a format that
   cannot, the **extension information is definitely lost**.

   When using :setting:`PHONENUMBER_DB_FORMAT`\ ``="NATIONAL"``, changing the
   :setting:`PHONENUMBER_DEFAULT_REGION` will cause phone numbers stored in the
   database to be interpreted differently, resulting in data corruption.

.. setting:: PHONENUMBER_DEFAULT_FORMAT

``PHONENUMBER_DEFAULT_FORMAT``
------------------------------

String formatting of phone numbers.

Default: ``"E164"``.

See :ref:`settings-format-choices`.

.. setting:: PHONENUMBER_DEFAULT_REGION

``PHONENUMBER_DEFAULT_REGION``
------------------------------

`ISO-3166-1 <https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes>`_
two-letter country code indicating how to interpret regional phone numbers.

Default: ``None``.