How to validate phone number in django?

10,442

Your validation looks correct to me, but you're doing two things wrong:

  • Rename the method to clean_phone_number() instead of clean_phone. The field is phone_number not phone.
  • Change the field to a CharField instead of IntegerField (too restrictive for many users) and return z.national_number instead of phone_number, that way it's returning the correctly cleaned number before saving. Or whatever format you want to store it in.
Share:
10,442
Jackson_Stake
Author by

Jackson_Stake

Updated on June 04, 2022

Comments

  • Jackson_Stake
    Jackson_Stake almost 2 years

    I'm currently using phonenumbers package as a validation method to my django's UserCreationForm for its phone number field. In my current code I am using a get method to retrieve the phone number from its field and then do the validation. If the entered number does not exists, a form error is supposed to pop up and state that the number is not in a country's format (in this case i'm using singapore). Please tell me what changes should be made to my current code.

    I've tried using "from phonenumber_field.formfields import PhoneNumberField" for the phone number validation and it validates the number I've entered, the only problem is that the users will have to type their country code and I cannot have that. I'm using just the phonenumbers package so that users will not have to enter the country code.

    /* forms.py */
    import phonenumbers
    from django import forms
    from django.contrib.auth.models import User
    from django.contrib.auth.forms import UserCreationForm
    from validate_email import validate_email
    from phonenumber_field.formfields import PhoneNumberField
    
    
    class UserRegisterForm(UserCreationForm):
        email = forms.EmailField()
        # phone_number = PhoneNumberField()
        phone_number = forms.IntegerField(required=True)
    
        class Meta:
            model = User
            fields = ['username', 'email', 'phone_number']
    
        def clean_email(self):
            email = self.cleaned_data.get("email")
            if not validate_email(email, verify=True):
                raise forms.ValidationError("Invalid email")
            return email
    
        def clean_phone(self):
            phone_number = self.cleaned_data.get("phone_number")
            z = phonenumbers.parse(phone_number, "SG")
            if not phonenumbers.is_valid_number(z):
                raise forms.ValidationError("Number not in SG format")
            return phone_number
    
    /* views.py */
    from django.shortcuts import render, redirect
    from django.contrib import messages
    from .forms import UserRegisterForm
    
    
    def register(request):
        if request.method == 'POST':
            form = UserRegisterForm(request.POST)
            if form.is_valid():
                # user = form.save()
                # phone_number = form.cleaned_data['phone']
                # do something with phone number??
                user = form.save()
                user.refresh_from_db()
                phone = form.cleaned_data.get('phone_number')
                user.Meta.phone_number = phone
                user.save()
                username = form.cleaned_data.get('username')
                messages.success(request, f'Account created for {username}!')
                return redirect('blog-home')
        else:
            form = UserRegisterForm()
        return render(request, 'users/register.html', {'form': form})
    

    I expect the output to validate the entered phone number in the phone field without its country code and only 8 digits and raise an error if that number does not exist.

    • Kostas Charitidis
      Kostas Charitidis over 4 years
      You need to make modifications eithe to your is_valid_number(z) or to your z variable by adding the code yourself in front of the number before validation. Provide your is_valid_number(z) function to help you more.
    • dirkgroten
      dirkgroten over 4 years
      You should call your method clean_phone_number() otherwise it won't be called. Apart from that, it should work, I've tried your code, works for me.
    • dirkgroten
      dirkgroten over 4 years
      I would also make your input field a CharField instead of IntegerField because people tend to add spaces or dashes in phone numbers. Your validation will take care of cleaning the number: don't return phone_number, but return z.national_number instead.
  • Jackson_Stake
    Jackson_Stake over 4 years
    Thank you its working for me, one question tho, can i write the return sentence to "return phonenumbers.format_number(z, phonenumbers.PhoneNumberFormat.E164)" instead. It might be better to convert the number to E164 format instead of local format.
  • Jackson_Stake
    Jackson_Stake over 4 years
    Does the region attribute only work for one specific region or all regions? Lets say other than SG, US can type in their numbers as well
  • Chris H
    Chris H over 4 years
    It will only work for the region you specify. Mainly because during the instantiation of the PhoneNumberField there is a validate_region function that is run. It will raise a ValueError if you pass a region that does not match its predefined list, though it will allow None. But not passing a region will automatically try to parse the number which does require a country code otherwise the number is considered invalid. It needs that country code so that it knows which regex to use for which region.
  • Chris H
    Chris H over 4 years
    It goes quite a bit deeper and more complex on how it decides that but this is about as far as I looked into it. Here is a link to where all the phone number metadata for each region is stored. Basically stores patterns and such for each region and it looks to be dynamically loaded in within the module. github.com/daviddrysdale/python-phonenumbers/tree/dev/python‌​/…