Coverage for audoma/drf/fields.py: 28%

1"""

2 This module is an override for default drf's field module.

3 Most of those fields, are providing additional example functionality,

4 and also has defined schema type.

5"""

6

7 import sys

8

9 import exrex

10 import phonenumbers

11 from djmoney.contrib.django_rest_framework import MoneyField

12 from drf_spectacular.types import OpenApiTypes

13 from drf_spectacular.utils import extend_schema_field

14 from phonenumber_field import serializerfields

15 from phonenumber_field.phonenumber import to_python

16 from rest_framework import fields

17 from rest_framework.fields import * # noqa: F403, F401

18

19 from django.core import validators

20

21 from audoma.example_generators import generate_lorem_ipsum

22 from audoma.mixins import (

23 ExampleMixin,

24 NumericExampleMixin,

25 RegexExampleMixin,

26)

27

28

29 field_names = [

30 "BooleanField",

31 "NullBooleanField",

32 "EmailField",

33 "SlugField",

34 "URLField",

35 "DateTimeField",

36 "DurationField",

37 "ChoiceField",

38 "MultipleChoiceField",

39 "FilePathField",

40 "FileField",

41 "ImageField",

42 "ListField",

43 "DictField",

44 "HStoreField",

45 "JSONField",

46 "ReadOnlyField",

47 "SerializerMethodField",

48] # pragma: no cover

49

50

51 this = sys.modules[__name__]

52

53

54 for field_name in field_names:

55

56 setattr(

57 this,

58 field_name,

59 type(field_name, (ExampleMixin, getattr(fields, field_name)), {}),

60 )

61

62

63 class DecimalField(NumericExampleMixin, fields.DecimalField):

64 pass

65

66

67@extend_schema_field(OpenApiTypes.UUID)

68 class UUIDField(ExampleMixin, fields.UUIDField):

69 pass

70

71

72 class IntegerField(NumericExampleMixin, fields.IntegerField):

73 pass

74

75

76 class FloatField(NumericExampleMixin, fields.FloatField):

77 pass

78

79

80 class RegexField(RegexExampleMixin, fields.RegexField):

81 pass

82

83

84 class MACAddressField(RegexExampleMixin, fields.CharField):

85 def __init__(self, **kwargs) -> None:

86 self.regex = "^([0-9A-F]{2}:){5}([0-9A-F]{2})|([0-9A-F]{2}-){5}([0-9A-F]{2})$"

87 self.validators = [validators.RegexValidator(self.regex)]

88 super().__init__(**kwargs)

89

90

91@extend_schema_field(OpenApiTypes.DATE)

92 class DateField(ExampleMixin, fields.DateField):

93 pass

94

95

96@extend_schema_field(OpenApiTypes.TIME)

97 class TimeField(ExampleMixin, fields.TimeField):

98 pass

99

100

101@extend_schema_field(

102 field={

103 "format": "ip-address",

104 "example": str(

105 exrex.getone(

106 r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"

107 )

108 ),

109 }

110)

111 class IPAddressField(ExampleMixin, fields.IPAddressField):

112 pass

113

114

115@extend_schema_field(field={"format": "tel"})

116 class PhoneNumberField(ExampleMixin, serializerfields.PhoneNumberField):

117 def __init__(self, *args, **kwargs) -> None:

118 example = kwargs.pop("example", None)

119 if example is None:

120 number = phonenumbers.example_number(None)

121 example = str(to_python(number))

122 super().__init__(*args, example=example, **kwargs)

123

124

125 class CharField(ExampleMixin, fields.CharField):

126 def __init__(self, *args, **kwargs) -> None:

127 example = kwargs.pop("example", None)

128 min_length = kwargs.get("min_length", 20)

129 max_length = kwargs.get("max_length", 80)

130 if not example:

131 example = generate_lorem_ipsum(min_length=min_length, max_length=max_length)

132

133 super().__init__(*args, example=example, **kwargs)

134

135

136 class MoneyField(NumericExampleMixin, MoneyField):

137 pass

138

139

140 class ChoiceField(ExampleMixin, fields.ChoiceField):

141 def __init__(self, choices, **kwargs):

142 if isinstance(choices, dict):

143 self.original_choices = list(choices.items())

144 else:

145 self.original_choices = choices

146 super().__init__(choices, **kwargs)