You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

phonenumber.py 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. # This module is based on django-phone-number-field
  2. # https://github.com/stefanfoulis/django-phonenumber-field
  3. #
  4. # Here is the relevant copyright and permissions notice.
  5. #
  6. # Copyright (c) 2011 Stefan Foulis and contributors.
  7. #
  8. # Permission is hereby granted, free of charge, to any person
  9. # obtaining a copy of this software and associated documentation
  10. # files (the "Software"), to deal in the Software without
  11. # restriction, including without limitation the rights to use,
  12. # copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. # copies of the Software, and to permit persons to whom the
  14. # Software is furnished to do so, subject to the following
  15. # conditions:
  16. import six
  17. from django.core import validators
  18. from django.conf import settings
  19. from django.core.exceptions import ValidationError
  20. from django.utils.translation import ugettext_lazy as _
  21. import phonenumbers
  22. class PhoneNumber(phonenumbers.phonenumber.PhoneNumber):
  23. """
  24. A extended version of phonenumbers.phonenumber.PhoneNumber that provides
  25. some neat and more pythonic, easy to access methods. This makes using a
  26. PhoneNumber instance much easier, especially in templates and such.
  27. """
  28. format_map = {
  29. 'E164': phonenumbers.PhoneNumberFormat.E164,
  30. 'INTERNATIONAL': phonenumbers.PhoneNumberFormat.INTERNATIONAL,
  31. 'NATIONAL': phonenumbers.PhoneNumberFormat.NATIONAL,
  32. 'RFC3966': phonenumbers.PhoneNumberFormat.RFC3966,
  33. }
  34. @classmethod
  35. def from_string(cls, phone_number, region=None):
  36. phone_number_obj = cls()
  37. if region is None:
  38. region = getattr(settings, 'PHONENUMBER_DEFAULT_REGION', None)
  39. phonenumbers.parse(number=phone_number, region=region,
  40. keep_raw_input=True, numobj=phone_number_obj)
  41. return phone_number_obj
  42. def __unicode__(self):
  43. format_string = getattr(
  44. settings, 'PHONENUMBER_DEFAULT_FORMAT', 'INTERNATIONAL')
  45. fmt = self.format_map[format_string]
  46. if self.is_valid():
  47. return self.format_as(fmt)
  48. return self.raw_input
  49. def __str__(self):
  50. return str(self.__unicode__())
  51. def is_valid(self):
  52. """
  53. checks whether the number supplied is actually valid
  54. """
  55. return phonenumbers.is_valid_number(self)
  56. def format_as(self, format):
  57. if self.is_valid():
  58. return phonenumbers.format_number(self, format)
  59. else:
  60. return self.raw_input
  61. @property
  62. def as_international(self):
  63. return self.format_as(phonenumbers.PhoneNumberFormat.INTERNATIONAL)
  64. @property
  65. def as_e164(self):
  66. return self.format_as(phonenumbers.PhoneNumberFormat.E164)
  67. @property
  68. def as_national(self):
  69. return self.format_as(phonenumbers.PhoneNumberFormat.NATIONAL)
  70. @property
  71. def as_rfc3966(self):
  72. return self.format_as(phonenumbers.PhoneNumberFormat.RFC3966)
  73. def __len__(self):
  74. return len(self.__unicode__())
  75. def __eq__(self, other):
  76. if type(other) == PhoneNumber:
  77. return self.as_e164 == other.as_e164
  78. else:
  79. return super(PhoneNumber, self).__eq__(other)
  80. def to_python(value):
  81. if value in validators.EMPTY_VALUES: # None or ''
  82. phone_number = None
  83. elif value and isinstance(value, six.string_types):
  84. try:
  85. phone_number = PhoneNumber.from_string(phone_number=value)
  86. except phonenumbers.phonenumberutil.NumberParseException:
  87. # the string provided is not a valid PhoneNumber.
  88. phone_number = PhoneNumber(raw_input=value)
  89. elif isinstance(value, PhoneNumber):
  90. phone_number = value
  91. return phone_number
  92. class PhoneNumberDescriptor(object):
  93. """
  94. The descriptor for the phone number attribute on the model instance.
  95. Returns a PhoneNumber when accessed so you can do stuff like::
  96. >>> instance.phone_number.as_international
  97. Assigns a phone number object on assignment so you can do::
  98. >>> instance.phone_number = PhoneNumber(...)
  99. or
  100. >>> instance.phone_number = '+414204242'
  101. """
  102. def __init__(self, field):
  103. self.field = field
  104. def __get__(self, instance=None, owner=None):
  105. if instance is None:
  106. raise AttributeError(
  107. "The '%s' attribute can only be accessed from %s instances."
  108. % (self.field.name, owner.__name__))
  109. return instance.__dict__[self.field.name]
  110. def __set__(self, instance, value):
  111. instance.__dict__[self.field.name] = to_python(value)
  112. def validate_international_phonenumber(value):
  113. phone_number = to_python(value)
  114. if phone_number and not phone_number.is_valid():
  115. raise ValidationError(_(u'The phone number entered is not valid.'))