- Сохранение телефонных номеров с помощью
PhoneNumberField
стороннего приложения Django - Сохранение телефонных номеров с помощью валидатора и модели Django
При создании базы данных мы часто сталкиваемся с проблемами, когда не можем определить тип поля для конкретных данных. Чаще всего мы не понимаем, как организовать некоторые типы полей. Например: если нам нужно сохранить уникальный номер, мы должны выбрать между символьным полем или целочисленным полем. У каждого типа поля есть свои преимущества и недостатки.
Одним из таких данных является номер телефона; сохраняя номер телефона, мы должны убедиться, что в поле можно хранить номера телефонов из любой точки мира. Мы должны убедиться, что мы храним код страны вместе с самим номером телефона.
В этой статье рассказывается о некоторых из лучших способов сохранить номер телефона в Django.
Сохранение телефонных номеров с помощью PhoneNumberField
стороннего приложения Django
Для хранения телефонных номеров мы можем использовать стороннее приложение или библиотеку Django, в которых реализовано это поле: PhoneNumberField
.
Вы можете найти репозиторий GitHub этой библиотеки или приложения здесь.
Согласно официальному README
, эта библиотека Django может проверять и преобразовывать телефонные номера. Эта библиотека взаимодействует с другой библиотекой Python, python-phonenumbers, которая является портом библиотеки Google libphonenumber, которая обеспечивает обработку телефонных номеров Android.
Установите библиотеку Django
Эту библиотеку Django можно загрузить с помощью следующей команды pip
:
pip install django-phonenumber-field[phonenumbers]
Поскольку это обширная библиотека с точки зрения размера, вы можете загрузить более легкую версию. Чтобы загрузить более легкую версию этой библиотеки, используйте команду ниже:
pip install django-phonenumber-field[phonenumberslite]
Настроить библиотеку Django
Чтобы использовать это приложение или библиотеку, мы должны добавить их в файл settings.py
нашего проекта. Имя приложения необходимо ввести в INSTALLED_APPS
следующим образом:
INSTALLED_APPS = [
# Other apps
"phonenumber_field",
]
Используйте библиотеку Django
В этой библиотеке есть поле модели PhoneNumberField
, которое можно использовать для хранения телефонных номеров. Наша модель будет выглядеть примерно так:
from phonenumber_field.modelfields import PhoneNumberField
class Person(models.Model):
firstName = models.CharField(max_length = 100)
middleName = models.CharField(max_length = 100)
lastName = models.CharField(max_length = 100)
phoneNumber = PhoneNumberField(unique = True, null = False, blank = False) # Here
secondPhoneNumber = PhoneNumberField(null = True, blank = True) # Here
Поле PhoneNumberField
внутренне основано на пространстве CharField
и хранит номера в виде строки в соответствии с международными стандартами телефонных номеров.
Чтобы узнать больше об этой библиотеке, обратитесь к официальной документации.
Теперь, чтобы получить доступ к значениям этого поля, мы напишем инструкцию Python, которая выглядит примерно так:
person = models.Person.objects.get(id = 25)
phoneNumber = person.phoneNumber.as_e164
Здесь номер телефона возвращается в виде строки в стандарте E.164
из-за as_e164
. E.164
— это международный стандарт хранения телефонных номеров. Вы можете прочитать больше об этом здесь].
Сохранение телефонных номеров с помощью валидатора и модели Django
Если вы не хотите использовать стороннее приложение Django и хранить телефонные номера с использованием моделей Django, вы можете использовать Validator
и CharField
. Следующий код показывает то же самое:
from django.core.validators import RegexValidator
class Person(models.Model):
phoneNumberRegex = RegexValidator(regex = r"^+?1?d{8,15}$")
phoneNumber = models.CharField(validators = [phoneNumberRegex], max_length = 16, unique = True)
Валидатор phoneNumberRegex
проверяет значение, введенное для CharField
. Опять же, номера телефонов хранятся в формате E.164
.
CharField
имеет максимальную длину 16 символов, потому что стандарт E.164
допускает максимум 15 цифр для номера. Пятнадцать символов включают код страны и номер телефона. Дополнительный символ зарезервирован для знака +, который является префиксом кода страны.
First, install «django-phonenumber-field» package with the command below:
pip install django-phonenumber-field[phonenumbers]
Then, set «phonenumber_field» to INSTALLED_APPS in «settings.py»:
# "settings.py"
INSTALLED_APPS = [
...
"phonenumber_field",
...
]
Then, set a field with «PhoneNumberField()» in «models.py»:
# "models.py"
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
class Contact(models.Model):
phone = PhoneNumberField()
Then, register «Contact» in «admin.py»:
# "admin.py"
from django.contrib import admin
from .models import Contact
@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
pass
Then, run the command below:
python manage.py makemigrations && python manage.py migrate
Now, the field for a phone number is created as shown below:
In addition, assign the widget «PhoneNumberPrefixWidget()» to the field in a custom form and assign the custom form to the admin as shown below:
# "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
Now, with country codes, the field for a phone number is created
And, you can set an initial country code like initial=’US’ to «PhoneNumberPrefixWidget()» as shown below. *Initial country code must be uppercase:
# "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
Now, with the initial country code «US» selected, the field for a phone number is created:
You can also set an initial country code with «PHONENUMBER_DEFAULT_REGION» in «settings.py» as shown below but I recommand to set an initial country code with initial=’US’ to «PhoneNumberPrefixWidget()» as I’ve done above because using «PHONENUMBER_DEFAULT_REGION» sometimes doesn’t display saved phone numbers in Django Admin:
# "settings.py"
PHONENUMBER_DEFAULT_REGION = "US"
Я работаю штатным программистом в Санкт-Петербургском Государственном Экономическом Университете и моей задачей с недавних пор является сопровождение деятельности отдела фото-видео производства (начинающее университетское телевидение). Как только начальник управления попросил всех зарегистрироваться в Bitrix24 с целью автоматизировать отчёты, я подумал об автоматизации входящих задач (на самом деле всё немного глубже и целью была дисциплина, но к посту не относится).
Собственно, первом делом я взялся за форму заявки на фото-видео съёмку, которую, недолго думая, набросал в Django. К этому моменту мои познания фрэймворка ограничивались несколькими представлениями для вывода списков на внутреннем сервере отдела. Основной сложность в форме стали
внезапно
поля — хотелось одновременно и красоту навести и от ошибок ввода пользователя уберечь(ся). Особенно интересовали меня два поля — поле ввода телефона и выбора времени. О них и речь.
Начну с поля для ввода телефона. Сначала я обратился к Хабру и нашёл некий пост, однако увиденное показалось мне слишком громоздким для моей цели (ведь мне надо было просто передать телефон оператору). Я понял, что использование CharField не приведёт ни к одной из моих целей и, пошуршав документацией Django, я нашёл там MultiValueField и MultiWidget в дополнение (сразу хочу сказать, что использование только MultiValueField порождает обычную строку для ввода текста, что никакого смысла не имеет). Через несколько минут был написан код под катом.
1) Для начала импортируем необходимое.
from django.forms import MultiValueField, CharField, ChoiceField, MultiWidget, TextInput, Select
2) Затем определяем PhoneWidget, базовым классом для которого будет MultiWidget.
class PhoneWidget(MultiWidget):
def __init__(self, code_length=3, num_length=7, attrs=None):
widgets = [TextInput(attrs={'size': code_length, 'maxlength': code_length}),
TextInput(attrs={'size': num_length, 'maxlength': num_length})]
super(PhoneWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [value.code, value.number]
else:
return ['', '']
Немного пояснений:
Длина кода города в Санкт-Петербурге и мобильных всея Руси — 3 символа, а самого телефона — 7. Но и тот и другой параметр могут меняться, поэтому они и были указаны в конструкторе с дефолтными значениями, подходящими для меня.
Далее идёт определение 2х самих виджетов (думаю, для понимания можно назвать их представлениями полей) с ограничениями длинны поля ввода (для красоты) и количества символов (для защиты от ошибок) параметрами code_length(длина кода) и num_length(длина номера).
Красоты
феншуя
ради я добавил ещё метод format_output()
def format_output(self, rendered_widgets):
return '+7' + '(' + rendered_widgets[0] + ') - ' + rendered_widgets[1]
дабы получить на выходе красивое форматирование в html.
3) Определяем само поле базовым классом для которого будет MultiValueField.
class PhoneField(MultiValueField):
def __init__(self, code_length, num_length, *args, **kwargs):
list_fields = [CharField(),
CharField()]
super(PhoneField, self).__init__(list_fields, widget=PhoneWidget(code_length, num_length), *args, **kwargs)
def compress(self, values):
return '+7' + values[0] + values[1] #Собственно, стандартизация строки номера эстетики ради
Немного пояснений:
В конструкторе содержатся те же параметры для передачи конструктору виджета. Иначе, как я уже писал, мы получим обычную строку для ввода текста.
4) В итоге в форме мы указываем поле
p_num = PhoneField()
#При желаниинеобходимости можно вызвать с параметрами: PhoneField(code_length=some_value, num_length=some_value)
и, добавив в шаблон
{{ form.p_num.errors }}
<label for="phone_num">Номер телефона:</label>
</br>
{{ form.p_num }}
мы получаем два красивых поля длинной 3 и 7 (или сколько указано) символов и международным кодом впереди для его стандартизации и подсказки полльзователю.
По аналогии были сделаны виджет и поле для ввода времени:
class TimeWidget(MultiWidget):
def __init__(self, h_choices, m_choices, attrs=None):
widgets = [Select(choices=h_choices),
Select(choices=m_choices)]
super(TimeWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [value.hours, value.minutes]
else:
return ['', '']
class TimeField(MultiValueField):
def __init__(self, h_choices, m_choices, *args, **kwargs):
list_fields = [ChoiceField(),
ChoiceField()]
super(TimeField, self).__init__(list_fields, widget=TimeWidget(h_choices, m_choices),*args, **kwargs)
def compress(self, values):
return return values[0] + ':' + values[1] #Стандартизация для приведения впоследствии к объекту datetime
Для защиты пользователя от указания времени вне рабочего дня, а также вне разумного лимита (24 и 60) я сделал 2 списка с выбором параметром.
Вызов:
time = TimeField(h_choices=HOURS_CHOICES, m_choices=MINUTES_CHOICES)
И сами параметры (в соответствии с документацией Django должны быть списком кортежей и определяться вне класса формы):
HOURS_CHOICES = [(str(x), x) for x in range(9, 21)]
#Даю на выбор лишь разумное рабочее время, чтобы не ворчали операторы
MINUTES_CHOICES = [(1, 0), (2, 10), (3, 20), (4, 30), (5, 40), (6, 50),]
#Первое число - порядковый номер, второе - значение. В списке будут только значения.
Осталось только понять, как между полями вставлять некие символьные разделители (например, ‘:’ между часами и минутами), но функциональной нагрузки это не несёт. Впрочем, я постараюсь решить и эту задачу и дополню пост найденным решением, а также другими примерами полей.
UPD: описан метод format_output() для получения
всяких скобочек рюшечек
опрятного форматирования.
I want to add PhoneNumberField so I can use take phone number of user at time registration. I already have UserCreationForm model which is used by CreateUserForm to create a new User. However by default it does not phone number field. I tried adding it by adding phone variable in forms.py but it does not work giving the error below.
forms.py
class CreateUserForm(UserCreationForm):
phone=PhoneNumberField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'phone']
models.py
class CustomerReg(models.Model):
user=models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
name=models.CharField(max_length=200, null=True)
email=models.EmailField(max_length=254)
phone=PhoneNumberField(default=None)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile=CustomerReg.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
This is Error I get when I run python manage.py makemigrations
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
File "manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagement__init__.py", line 401, in execute_from_command_line
utility.execute()
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagement__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 330, in run_from_argv
self.execute(*args, **cmd_options)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 368, in execute
self.check()
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 396, in check
databases=databases,
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksregistry.py", line 70, in run_checks
new_errors = check(app_configs=app_configs, databases=databases)
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksurls.py", line 13, in check_url_config
return check_resolver(resolver)
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksurls.py", line 23, in check_resolver
return check_method()
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 408, in check
for pattern in self.url_patterns:
File "D:djangoL2GvenvPlibsite-packagesdjangoutilsfunctional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 589, in url_patterns
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "D:djangoL2GvenvPlibsite-packagesdjangoutilsfunctional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 582, in urlconf_module
return import_module(self.urlconf_name)
File "C:PythonPython36libimportlib__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "D:DjangoL2Gwizl2gprojectl2gProjecturls.py", line 23, in <module>
path('',include('customer.urls')),
File "D:djangoL2GvenvPlibsite-packagesdjangourlsconf.py", line 34, in include
urlconf_module = import_module(urlconf_module)
File "C:PythonPython36libimportlib__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "D:DjangoL2Gwizl2gprojectcustomerurls.py", line 2, in <module>
from . import views
File "D:DjangoL2Gwizl2gprojectcustomerviews.py", line 6, in <module>
from .forms import CreateUserForm
File "D:DjangoL2Gwizl2gprojectcustomerforms.py", line 35, in <module>
class CreateUserForm(UserCreationForm):
File "D:djangoL2GvenvPlibsite-packagesdjangoformsmodels.py", line 268, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (phone) specified for User
How do I add phone number field to my form then?
I want to add PhoneNumberField so I can use take phone number of user at time registration. I already have UserCreationForm model which is used by CreateUserForm to create a new User. However by default it does not phone number field. I tried adding it by adding phone variable in forms.py but it does not work giving the error below.
forms.py
class CreateUserForm(UserCreationForm):
phone=PhoneNumberField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'phone']
models.py
class CustomerReg(models.Model):
user=models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
name=models.CharField(max_length=200, null=True)
email=models.EmailField(max_length=254)
phone=PhoneNumberField(default=None)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile=CustomerReg.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
This is Error I get when I run python manage.py makemigrations
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
File "manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagement__init__.py", line 401, in execute_from_command_line
utility.execute()
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagement__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 330, in run_from_argv
self.execute(*args, **cmd_options)
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 368, in execute
self.check()
File "D:djangoL2GvenvPlibsite-packagesdjangocoremanagementbase.py", line 396, in check
databases=databases,
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksregistry.py", line 70, in run_checks
new_errors = check(app_configs=app_configs, databases=databases)
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksurls.py", line 13, in check_url_config
return check_resolver(resolver)
File "D:djangoL2GvenvPlibsite-packagesdjangocorechecksurls.py", line 23, in check_resolver
return check_method()
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 408, in check
for pattern in self.url_patterns:
File "D:djangoL2GvenvPlibsite-packagesdjangoutilsfunctional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 589, in url_patterns
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "D:djangoL2GvenvPlibsite-packagesdjangoutilsfunctional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "D:djangoL2GvenvPlibsite-packagesdjangourlsresolvers.py", line 582, in urlconf_module
return import_module(self.urlconf_name)
File "C:PythonPython36libimportlib__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "D:DjangoL2Gwizl2gprojectl2gProjecturls.py", line 23, in <module>
path('',include('customer.urls')),
File "D:djangoL2GvenvPlibsite-packagesdjangourlsconf.py", line 34, in include
urlconf_module = import_module(urlconf_module)
File "C:PythonPython36libimportlib__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "D:DjangoL2Gwizl2gprojectcustomerurls.py", line 2, in <module>
from . import views
File "D:DjangoL2Gwizl2gprojectcustomerviews.py", line 6, in <module>
from .forms import CreateUserForm
File "D:DjangoL2Gwizl2gprojectcustomerforms.py", line 35, in <module>
class CreateUserForm(UserCreationForm):
File "D:djangoL2GvenvPlibsite-packagesdjangoformsmodels.py", line 268, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (phone) specified for User
How do I add phone number field to my form then?
Сайт телефонный корпоративный справочник
Сайт телефонный корпоративный справочник написан на фреймворке python Django, используется по умолчанию СУБД SQLite3. В данном проекте используются:
- одно приложение ‘phonebook’, применяется двухуровневый шаблон страниц (с подгрузкой навбара и сайдбара);
- подключена статика bootstrap в локальном режиме (позволяет запустить сервер офлайн) все элементы сохранят свой вид;
- реализованы пользовательские теги;
- реализованы формы заполнения данных;
- реализована регистрация, вход, аутентификция и разграничение прав пользователей;
- реализована пагинация данных;
- применяются мексины;
- подключено приложение для отладки сайта DebagToolbar.
Содержание проекта:
-
папка data — место хранения исходных капч (5 цифр)
-
README.md — описание проекта
-
update_pakets.py — обновление пакетов проекта в автоматическом режиме
-
django_command — описание команд python при подготовке проекта и его запуска
-
for_mysql.py — проверка подключения к базе данных MySQL
-
папка phonebooke_site — размещение проекта
-
db.sqlite3 — база данных sqlite3
-
manage.py — основной исполняемый файл сайта
-
my.cnf — конфигурация MySQL
-
mySQL_command — команды MySQL
-
django_cache — папка для хранения кеша
-
media — папка хранения медиафайлов (фотографий)
-
phonebook — папка приложения телефонный справочник
-
phonebooke_site — папка размещения основы сайта
-
static — папка для размещения статических файлов
-
templates — папка хранения htmal страниц
Библиотеки применяемые в проекте:
- settings — подгружаем настройки в режиме отладки
- static, path, include, reverse_lazy — для работы с статическими, ленивыми и url адресами
- forms — модуль работы с формами
- re — модуль регулярных выражений, для работы с данными в форме (для применения собственных проверок
- ValidationError — модуль обработок собственных ошибок
- UserCreationForm, AuthenticationForm — модуль регистрации пользователя и логирования
- User — модуль работы с пользователями
- send_mail — модуль отправки сообщений
- models — импортируем модуль работы с базами данных
- LoginRequiredMixin — импортируем штатный миксин проверки аутентификации
- Paginator — пагинация страниц
- UserCreationForm — создание формы регистрации пользователя
- messages — модуль сообщений
- login, logout — модуль логирования и выхода
- send_mail — модуль отправки сообщений
- Count, F — модуль подсчета значений и фильтрации
Клонируем репозиторий (Clone the repository)
$ cmd
$ git clone https://github.com/BEPb/first.git
$ cd django-phonebook
Устанавливаем необходимые пакеты (Install dependencies)
$ pip install -r requirements.txt / pipenv install
Запускаем локальный сервер (Run the application locally)
$ python manage.py runserver
Открывает Ваш браузер по адресу http://localhost:8000/
Вопрос:
Я храню номер телефона в model
как это:
phone_number = models.CharField(max_length=12)
Пользователь будет вводить номер телефона, и я буду использовать номер телефона для SMS Authentication
Это приложение будет использоваться во всем мире. Поэтому мне также понадобится код страны. Является ли CharField
хорошим способом хранения номера телефона? И как мне проверить номер телефона?
Заранее спасибо.
Лучший ответ:
Вы можете взглянуть на международный стандартизированный формат E.164, рекомендованный, например, Twilio (у которого есть сервис и API для отправки SMS или телефонных звонков через запросы REST).
Это, вероятно, самый универсальный способ хранения телефонных номеров, особенно если вы работаете с международными номерами.
1. Телефон по PhoneNumberField
Вы можете использовать библиотеку phonenumber_field
. Это порт библиотеки Google libphonenumber, которая обеспечивает обработку телефонных номеров Android https://github.com/stefanfoulis/django-phonenumber-field
В модели:
from phonenumber_field.modelfields import PhoneNumberField
class Client(models.Model, Importable):
phone = PhoneNumberField(null=False, blank=False, unique=True)
Сообщить:
from phonenumber_field.formfields import PhoneNumberField
class ClientForm(forms.Form):
phone = PhoneNumberField()
Получить телефон в виде строки из поля объекта:
client.phone.as_e164
Нормализировать телефонную строку (для тестов и другого персонала):
from phonenumber_field.phonenumber import PhoneNumber
phone = PhoneNumber.from_string(phone_number=raw_phone, region='RU').as_e164
2. Телефон по регулярному выражению
Одно замечание для вашей модели: максимальная длина символа E.164 равна 15.
Для проверки вы можете использовать некоторую комбинацию форматирования, а затем сразу же пытаться связаться с номером для подтверждения.
Я думаю, что я использовал что-то вроде следующего в моем проекте django:
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 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
Ответ №1
Ответ №2
Ответ №3
Проверка проста, назовите их небольшим кодом для ввода. CharField – отличный способ его сохранить. Я не стал бы слишком беспокоиться о канонизации номеров телефонов.
Ответ №4
Все зависит от того, что вы понимаете как номер телефона. Номера телефонов специфичны для страны. Пакеты localflavors для нескольких стран содержат свое собственное поле “номер телефона”. Так что, если вы в порядке, специфические для страны, вы должны взглянуть на пакет localflavor (class us.models.PhoneNumberField
для случая в США и т.д.).
В противном случае вы можете проверить локальных злоумышленников, чтобы получить максимальную длину для всех стран. Localflavor также имеет поля форм, которые вы могли бы использовать в сочетании с кодом страны для подтверждения номера телефона.
Ответ №5
Я опишу, что я использую:
Валидация: строка содержит более 5 цифр.
Очистка: удаление всех символов без цифр, запись только в числах. Мне повезло, потому что в моей стране (Россия) у всех есть номера телефонов с 10 цифрами. Поэтому я храню в db всего 10 децитов. Если вы пишете многостраничное приложение, вы должны сделать всестороннюю проверку.
Рендеринг: я пишу специальный тег шаблона, чтобы сделать его в шаблоне красиво. Или даже сделать это как изображение – безопаснее предотвращать спам-спам.
Ответ №6
#python #django #django-models #django-views #django-forms
Вопрос:
Итак, текущая проблема, с которой я сталкиваюсь, заключается в том, что в настоящее время у меня есть форма для создания пользователя, которую мы сейчас добавляем в опцию номера телефона для этого, в настоящее время создается пользователь и профиль пользователя, но в профиле пользователя не сохранен номер телефона. Единственный способ, которым я смог его ввести, — это вручную через страницу администратора django
Я попробовал несколько вариантов, но ни один из них пока не работает. Я ценю любую помощь, которую могу получить
#это в моем forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
#fields = UserCreationForm.Meta.fields ('username', 'email', 'password1', 'password2')
def clean(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
raise forms.ValidationError('Email addresses must be unique.')
return super().clean()
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['phone_number']
это мое views.py
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
profile_form = UserProfileForm(request.POST) #added
if form.is_valid() and profile_form.is_valid(): #addded profile form
form.save()
profile_form.save() #added
messages.success(request, 'Account created!')
return redirect('login')
else:
form = UserRegisterForm()
profile_form = UserProfileForm() #added
return render(request, 'user/register.html', {'form': form, 'profile_form': profile_form})
это мое register.html
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Register</legend>
{{ form|crispy }}
<!-- {{ profile_form.as_p }} -->
</fieldset>
<div class="form-group">
<button type="submit" class="btn btn-outline-info">Sign Up</button>
</div>
</form>
this is my models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
qr_token = models.CharField(max_length=24, unique=True)
image = models.ImageField(default='default.png', upload_to='profile_pics')
date_created = models.DateTimeField(auto_now_add=True)
is_before_user = models.BooleanField(default=True)
phone_number = PhoneField(null=True, help_text='Contact phone number')
#there is more of it but the rest isnt relevent