Coverage for api/serializers/fields.py: 94%

30 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 06:14 +0000

1import re 

2 

3from django.conf import settings 

4from rest_framework import serializers 

5 

6from api.utils.help_text import make_comma_separated_help_text 

7 

8 

9class SchemableHyperlinkedIdentityField(serializers.HyperlinkedIdentityField): 

10 """ 

11 This field returns the link but allows the option to replace the URL scheme. 

12 

13 This is useful when the API runs on ``http`` behind a proxy that serves ``https``. 

14 In these cases, the scheme in hyperlinks must be forced to ``https``. 

15 """ 

16 

17 def __init__(self, scheme=settings.API_LINK_SCHEME, *args, **kwargs): 

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

19 

20 self.scheme = scheme 

21 

22 def get_url(self, *args, **kwargs): 

23 url = super().get_url(*args, **kwargs) 

24 

25 # Only rewrite URLs if a fixed scheme is provided 

26 if self.scheme is not None: 26 ↛ 27line 26 didn't jump to line 27 because the condition on line 26 was never true

27 url = re.sub(r"^\w+://", f"{self.scheme}://", url, 1) 

28 

29 return url 

30 

31 

32class EnumCharField(serializers.CharField): 

33 """This field extends the ``CharField`` to add enum validation.""" 

34 

35 default_error_messages = serializers.CharField.default_error_messages | { 

36 "outside_enum": "Invalid value: {given}. Allowed values: {allowed}" 

37 } 

38 

39 def __init__(self, plural: str, enum_class: set[str], **kwargs): 

40 kwargs["help_text"] = make_comma_separated_help_text(enum_class, plural) 

41 super().__init__(**kwargs) 

42 

43 self.enum_class = enum_class 

44 

45 def _validate_enum(self, given_value: str): 

46 """ 

47 Validate whether the given values are all members of the given enum. 

48 

49 :param given_value: the comma separated list received in the input 

50 :return: the lower cased form of the input, if the input is valid 

51 :raise: ``ValidationError``, if the input is invalid 

52 """ 

53 

54 lower = given_value.lower() 

55 input_values = lower.split(",") 

56 for value in input_values: 

57 if value not in self.enum_class: 

58 self.fail("outside_enum", given=value, allowed=self.enum_class) 

59 return lower 

60 

61 def to_internal_value(self, data): 

62 data = super().to_internal_value(data) 

63 self._validate_enum(data) 

64 return data