Django Phone Number Field
django-phonenumber-field is a Django library that integrates Google's `libphonenumber` (via `python-phonenumbers`) to provide an international phone number field for Django models and forms. It handles validation, formatting, and conversion of phone numbers according to international standards. The current version is 8.4.0, and the library maintains an active release cadence with multiple minor/patch releases throughout the year.
Common errors
-
ModuleNotFoundError: No module named 'phonenumber_field'
cause This error typically means the `django-phonenumber-field` package, or its underlying `phonenumbers` dependency, was not correctly installed or is not accessible in the Python environment where Django is running. The package itself is installed as `django-phonenumber-field` but imported as `phonenumber_field`.fixEnsure the package is installed using `pip install django-phonenumber-field[phonenumbers]` (or `django-phonenumber-field[phonenumberslite]` for a lighter version), and `phonenumber_field` is added to `INSTALLED_APPS` in your Django `settings.py`. -
Enter a valid phone number (e.g. +12125552368) or a number with an international call prefix.
cause This validation error occurs when a user enters a national phone number without an international prefix, and the `PHONENUMBER_DEFAULT_REGION` setting is not defined in Django's `settings.py`, or it's set to a region different from the entered number.fixSet `PHONENUMBER_DEFAULT_REGION = 'US'` (or your desired ISO 3166-1 two-letter country code) in your `settings.py` file to provide a default region for national number parsing, or ensure users always enter numbers with an international prefix (e.g., `+12125552368`). -
AttributeError: 'PhoneNumberField' object has no attribute 'as_international'
cause This error happens when you try to access formatting properties like `as_international`, `as_national`, `as_e164`, or `as_rfc3966` directly on the model field instance (`my_model.phone_number`) rather than on the `PhoneNumber` object that the field stores or returns.fixThe `PhoneNumberField` on a model returns a `PhoneNumber` object. Access the formatting properties on this object. For example, if `phone_number` is your field, use `my_instance.phone_number.as_international`. -
module 'django.forms' has no attribute 'PhoneNumberField'
cause This error indicates that you are attempting to import `PhoneNumberField` from `django.forms`, but the field provided by `django-phonenumber-field` is not part of Django's core forms module.fixImport `PhoneNumberField` from the correct location: `from phonenumber_field.formfields import PhoneNumberField` when defining forms, or `from phonenumber_field.modelfields import PhoneNumberField` when defining model fields.
Warnings
- breaking The `PhoneNumberInternationalFallbackWidget` was removed in version 8.0.0. If you were using it, you should switch to `phonenumber_field.widgets.RegionalPhoneNumberWidget` instead.
- breaking In version 8.0.0, validation logic was moved from widgets to form fields. This changes how custom validation on `PhoneNumberField`s works. If you had `clean_FIELD()` or `clean()` methods that relied on the widget's previous validation behavior, you may need to review and adjust them. Error codes like `invalid_phone_number` might need to be changed to `invalid`.
- breaking Version 7.2.0 introduced a database converter for the model field. This change, though intended as a fix for `values_list()` behavior, was retrospectively considered a breaking change as it could affect existing code.
- gotcha The `PHONENUMBER_DEFAULT_REGION` setting is critical for the library to correctly interpret and validate national phone numbers. Without it, numbers in 'NATIONAL' format may not be recognized or validated properly.
- gotcha When using `SplitPhoneNumberField` (which renders a separate input for the prefix and the national number), applying default CSS frameworks like Bootstrap might cause the prefix and number fields to display on separate lines instead of inline.
- gotcha While `PhoneNumberField` is based on `CharField` internally, it stores and retrieves `PhoneNumber` objects, not plain strings. When directly assigning a string value to a `PhoneNumberField` in Python code (e.g., `model_instance.phone_number = '+1234567890'`), the string must include the country code for correct parsing and validation.
Install
-
pip install django-phonenumber-field[phonenumbers] -
pip install django-phonenumber-field[phonenumberslite]
Imports
- PhoneNumberField
from phonenumber_field.modelfields import PhoneNumberField
- PhoneNumberField (form field)
from phonenumber_field.formfields import PhoneNumberField
- SplitPhoneNumberField (form field for prefix/number split input)
from phonenumber_field.formfields import SplitPhoneNumberField
- PhoneNumber
from phonenumber_field.phonenumber import PhoneNumber
- RegionalPhoneNumberWidget
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
from phonenumber_field.widgets import RegionalPhoneNumberWidget
Quickstart
import os
import django
from django.conf import settings
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
settings.configure(
INSTALLED_APPS=['phonenumber_field'],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
PHONENUMBER_DEFAULT_REGION='US' # Crucial for national formats
)
django.setup()
class Contact(models.Model):
name = models.CharField(max_length=100)
phone_number = PhoneNumberField(blank=True)
def __str__(self):
return f"{self.name}: {self.phone_number}"
# Example Usage:
# from django.core.management import call_command
# call_command('makemigrations', 'myapp') # if using in a real app
# call_command('migrate') # if using in a real app
contact1 = Contact.objects.create(name="Alice", phone_number="+12025550123")
contact2 = Contact.objects.create(name="Bob", phone_number="6502530000") # Assumes PHONENUMBER_DEFAULT_REGION='US'
print(contact1)
print(contact2)
print(contact1.phone_number.as_e164)
print(contact2.phone_number.as_national)
# Example with an invalid number (will raise ValidationError in a real form/model save)
# try:
# Contact.objects.create(name="Invalid", phone_number="123")
# except Exception as e:
# print(f"Caught expected error: {e}")