Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/phonenumber_field/modelfields.py: 82%

66 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from django.conf import settings 

2from django.core import checks 

3from django.db import models 

4from django.utils.encoding import force_str 

5from django.utils.translation import gettext_lazy as _ 

6 

7from phonenumber_field import formfields 

8from phonenumber_field.phonenumber import PhoneNumber, to_python, validate_region 

9from phonenumber_field.validators import validate_international_phonenumber 

10 

11 

12class PhoneNumberDescriptor: 

13 """ 

14 The descriptor for the phone number attribute on the model instance. 

15 Returns a PhoneNumber when accessed so you can do stuff like:: 

16 

17 >>> instance.phone_number.as_international 

18 

19 Assigns a phone number object on assignment so you can do:: 

20 

21 >>> instance.phone_number = PhoneNumber(...) 

22 

23 or, 

24 

25 >>> instance.phone_number = '+414204242' 

26 """ 

27 

28 def __init__(self, field): 

29 self.field = field 

30 

31 def __get__(self, instance, owner): 

32 if instance is None: 32 ↛ 33line 32 didn't jump to line 33, because the condition on line 32 was never true

33 return self 

34 

35 # The instance dict contains whatever was originally assigned in 

36 # __set__. 

37 if self.field.name in instance.__dict__: 37 ↛ 40line 37 didn't jump to line 40, because the condition on line 37 was never false

38 value = instance.__dict__[self.field.name] 

39 else: 

40 instance.refresh_from_db(fields=[self.field.name]) 

41 value = getattr(instance, self.field.name) 

42 return value 

43 

44 def __set__(self, instance, value): 

45 instance.__dict__[self.field.name] = to_python(value, region=self.field.region) 

46 

47 

48class PhoneNumberField(models.CharField): 

49 attr_class = PhoneNumber 

50 descriptor_class = PhoneNumberDescriptor 

51 default_validators = [validate_international_phonenumber] 

52 

53 description = _("Phone number") 

54 

55 def __init__(self, *args, region=None, **kwargs): 

56 """ 

57 :keyword str region: 2-letter country code as defined in ISO 3166-1. 

58 When not supplied, defaults to :setting:`PHONENUMBER_DEFAULT_REGION` 

59 :keyword int max_length: The maximum length of the underlying char field. 

60 """ 

61 kwargs.setdefault("max_length", 128) 

62 super().__init__(*args, **kwargs) 

63 self._region = region 

64 

65 @property 

66 def region(self): 

67 return self._region or getattr(settings, "PHONENUMBER_DEFAULT_REGION", None) 

68 

69 def check(self, **kwargs): 

70 errors = super().check(**kwargs) 

71 errors.extend(self._check_region()) 

72 return errors 

73 

74 def _check_region(self): 

75 try: 

76 validate_region(self.region) 

77 except ValueError as e: 

78 return [checks.Error(force_str(e), obj=self)] 

79 return [] 

80 

81 def get_prep_value(self, value): 

82 """ 

83 Perform preliminary non-db specific value checks and conversions. 

84 """ 

85 if not value: 

86 return super().get_prep_value(value) 

87 

88 if isinstance(value, PhoneNumber): 88 ↛ 92line 88 didn't jump to line 92, because the condition on line 88 was never false

89 parsed_value = value 

90 else: 

91 # Convert the string to a PhoneNumber object. 

92 parsed_value = to_python(value) 

93 

94 if parsed_value.is_valid(): 94 ↛ 101line 94 didn't jump to line 101, because the condition on line 94 was never false

95 # A valid phone number. Normalize it for storage. 

96 format_string = getattr(settings, "PHONENUMBER_DB_FORMAT", "E164") 

97 fmt = PhoneNumber.format_map[format_string] 

98 value = parsed_value.format_as(fmt) 

99 else: 

100 # Not a valid phone number. Store the raw string. 

101 value = parsed_value.raw_input 

102 

103 return super().get_prep_value(value) 

104 

105 def contribute_to_class(self, cls, name, *args, **kwargs): 

106 super().contribute_to_class(cls, name, *args, **kwargs) 

107 setattr(cls, self.name, self.descriptor_class(self)) 

108 

109 def deconstruct(self): 

110 name, path, args, kwargs = super().deconstruct() 

111 kwargs["region"] = self._region 

112 return name, path, args, kwargs 

113 

114 def formfield(self, **kwargs): 

115 defaults = { 

116 "form_class": formfields.PhoneNumberField, 

117 "region": self.region, 

118 "error_messages": self.error_messages, 

119 } 

120 defaults.update(kwargs) 

121 return super().formfield(**defaults)