在Django模型中存储电话号码的最佳方法是什么?

215

我像这样在model中存储电话号码:

phone_number = models.CharField(max_length=12)

用户将输入电话号码,我将使用该电话号码进行 SMS身份验证。该应用程序将在全球范围内使用。因此,我还需要一个国家代码。使用 CharField 存储电话号码是一种好方法吗?另外,我如何验证电话号码?


1
这里有一个很好的建议:https://dev59.com/InVD5IYBdhLWcg3wAGiD#1245990 - Victor Sergienko
11个回答

410
你可能需要了解国际标准格式E.164,例如推荐的Twilio服务和API,可以通过REST请求发送短信或电话。 如果你要处理国际号码,这很可能是最普遍的存储电话号码的方式。
  1. Phone by PhoneNumberField

    You can use the phonenumber_field library. It is a port of Google's libphonenumber library, which powers Android's phone number handling. See django-phonenumber-field.

    In the model:

    from phonenumber_field.modelfields import PhoneNumberField
    
    class Client(models.Model, Importable):
        phone = PhoneNumberField(null=False, blank=False, unique=True)
    

    In the form:

    from phonenumber_field.formfields import PhoneNumberField
    class ClientForm(forms.Form):
        phone = PhoneNumberField()
    

    Get the phone as a string from an object field:

    client.phone.as_e164
    

    Normalize the phone string (for tests and other staff):

    from phonenumber_field.phonenumber import PhoneNumber
    phone = PhoneNumber.from_string(phone_number=raw_phone, region='RU').as_e164
    
  2. Phone by regexp

    One note for your model: E.164 numbers have a maximum character length of 15.

    To validate, you can employ some combination of formatting and then attempting to contact the number immediately to verify.

    I believe I used something like the following in my django project:

     class ReceiverForm(forms.ModelForm):
         phone_number = forms.RegexField(regex=r'^\+?1?\d{9,15}$',
                                         error_message = ("Phone number must be entered in the format: '+999999999'. Up to 15 digits is allowed."))
    

根据 jpotter6 的说法,您也可以在模型中执行以下操作:
文件 models.py:
from django.core.validators import RegexValidator

class PhoneModel(models.Model):
    ...
    phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
    phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True) # Validators should be a list

27
可能需要演示如何在模型上执行此操作才更有价值。phone_regex = RegexValidator(regex=r'^+?1?\d{9,15}$', message="电话号码必须按照格式输入:'+999999999'。最多允许15个数字。")phone_number = models.CharField(validators=phone_regex, blank=True) - jpotts18
14
别忘了在 models.CharField 中添加 'max_length=15'。 - Esteban
4
@Esteban - max_length=16(开头可以选择性地加上一个 +)。@Esteban - max_length=16(开头可选加+)。 - dhackner
3
@erewok - 我认为你的正则表达式中不需要包含数字 '1'。根据 E.164 标准,电话号码最多可以有15位数字,这包括国家代码在内。 - dhackner
1
事实上,E.164不包括任何国际呼叫前缀,只有国家代码后跟实际号码,此外E.164号码的最小长度似乎为8,因此正则表达式应该是'^+\d{8,15}$',然后对于CharFieldmax_length=16 - Maxime R.
显示剩余3条评论

65

4
不要忘记添加地区。如果在设置中没有加入“PHONENUMBER_DEFAULT_REGION = 'US'”,它将不允许你输入美国用户可以识别为电话号码的任何内容。即使这样,该程序包也无法输出看起来像电话号码的内容......我相信还有一些我尚未发现的设置! - Drigan
15
请注意:这个软件包非常大,超过40MB。其中33MB是地理数据。 - Forethinker
5
注意@Forethinker的评论,一个更轻量级的替代品是https://github.com/VeryApt/django-phone-field,可以通过'pip install django-phone-field'进行安装。 - SMX
9
如果需要去除地理编码、运营商和时区元数据,可以使用“lite”版本 pip install django-phonenumber-field[phonenumberslite]。请注意不要改变原文意思,尽可能使翻译通俗易懂。 - heyxh

9

我现在同意@rgenito的观点(对于Django 3.x),并且我目前正在使用django-phonenumber-field(与phonenumbers一起)。 - nicorellius
1
链接已损坏。 - cdrrr

8
首先,使用下面的命令安装"django-phonenumber-field"包:
pip install django-phonenumber-field[phonenumbers]

然后,在"settings.py"文件中将"phonenumber_field"添加到"INSTALLED_APPS"中:
# "settings.py"

INSTALLED_APPS = [
    ...
    "phonenumber_field",
    ...
]

接下来,在 "models.py" 中使用 "PhoneNumberField()" 定义一个字段:

# "models.py"

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

class Contact(models.Model):
    phone = PhoneNumberField()

接下来,在"admin.py"中注册"Contact":

# "admin.py"

from django.contrib import admin
from .models import Contact

@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
    pass

接下来,运行以下命令:

python manage.py makemigrations && python manage.py migrate

现在,电话号码的字段创建如下所示:

enter image description here

此外,在自定义表单中将小部件“PhoneNumberPrefixWidget()”分配给该字段,并将自定义表单分配给管理员,如下所示:

# "admin.py"

from django.contrib import admin
from .models import Contact
from django import forms
from phonenumber_field.widgets import PhoneNumberPrefixWidget

class ContactForm(forms.ModelForm):
    class Meta:
        widgets = {
            'phone': PhoneNumberPrefixWidget(),
        }

@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
    form = ContactForm

现在,有了国家代码,电话号码字段就被创建了。

enter image description here

而且,您可以像下面所示设置一个初始国家代码 initial='US'"PhoneNumberPrefixWidget()"。*初始国家代码必须大写:

# "admin.py"

from django.contrib import admin
from .models import Contact
from django import forms
from phonenumber_field.widgets import PhoneNumberPrefixWidget

class ContactForm(forms.ModelForm):
    class Meta:
        widgets = {                          # Here
            'phone': PhoneNumberPrefixWidget(initial='US'),
        }

@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
    form = ContactForm

现在,选择了初始国家代码“US”之后,将创建一个电话号码字段:

enter image description here

您还可以像下面展示的那样,在“settings.py”中使用"PHONENUMBER_DEFAULT_REGION"设置初始国家代码,但我建议您像我上面所做的那样,通过将initial='US'添加到"PhoneNumberPrefixWidget()"来设置初始国家代码,因为有时在Django Admin中使用"PHONENUMBER_DEFAULT_REGION"不能正确显示已保存的电话号码:

# "settings.py"

PHONENUMBER_DEFAULT_REGION = "US"

1
谢谢,这真的很有帮助。它完全按照我想要的方式工作。 - Abayomi Olowu

8

验证很简单。给他们发送一些代码进行输入即可。

CharField是存储它的好方法。我不会太担心规范化电话号码。


最佳答案:让客户端进行验证。 - user3053446

8
这个解决方案对我很有帮助:
首先安装django-phone-field,命令:
pip install django-phone-field

接下来在文件models.py中:

from phone_field import PhoneField
...

class Client(models.Model):
    ...
    phone_number = PhoneField(blank=True, help_text='Contact phone number')

settings.py文件中:

INSTALLED_APPS = [...,
                  'phone_field'
]

最终效果如下:

表格中的电话


(注:此处译为“表格”而非“表单”,因为根据上下文猜测,“表格”更贴近原意。)

你好,如何删除外部输入表单?我只想要联系电话号码,不需要分机号。 - Fahmi Sabila Dinnulhaq

3

其他人提到了 django-phonenumber-field 。如果想要按照自己的方式显示该格式,需要将 PHONENUMBER_DEFAULT_FORMAT 设置为 "E164", "INTERNATIONAL", "NATIONAL" 或者 "RFC3966"。请参考GitHub源代码


1

首先,您需要安装电话号码Django包。

pip install django-phonenumber-field[phonenumbers]

接下来是将其添加到已安装的应用程序中:
INSTALLED_APPS = [
    ...
    'phonenumber_field',
    ...
]

这是如何在你的模型中使用它:
from phonenumber_field.modelfields import PhoneNumberField

class Artist(models.Model):

    phone_number = PhoneNumberField()

1
这完全取决于您对电话号码的理解。电话号码是与国家相关的。几个国家的localflavors包含了它们自己的“电话号码字段”。因此,如果您可以接受特定于国家/地区的情况,则应查看localflavor包(例如,在美国情况下使用类)。
否则,您可以检查localflavors以获取所有国家/地区的最大长度。Localflavor还有表单字段,您可以将其与国家/地区代码一起使用以验证电话号码。

嗯,是的,我在研究时发现了这个。你能举个例子说明如何在表单中使用它吗? - pynovice

1
我将描述我使用的内容:
验证: 字符串包含超过5个数字。
清理: 删除所有非数字符号,并将仅数字写入数据库。我很幸运,因为在我的国家(俄罗斯),每个人的电话号码都有10个数字。所以我只在数据库中存储10个数字。如果您正在编写一个多国应用程序,则应进行全面验证。
渲染: 我编写了一个自定义模板标签,以便在模板中美观地呈现它。甚至可以像图片一样呈现它-这样更安全,可以防止短信垃圾邮件。

几年后偶然发现这篇文章,但是根据平台(在这种情况下,我认为是Web),我反对将文本呈现为图片,即使它有助于防止垃圾邮件。这样非常不可访问。它无法复制粘贴,无法缩放并保持清晰度,也无法被屏幕阅读器读取。https://www.boia.org/blog/why-is-it-important-for-accessibility-to-use-actual-text-instead-of-images-of-text - Caleb Jay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接