Coverage for apis_core/apis_entities/models.py: 62%

56 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-09-03 06:15 +0000

1import functools 

2import logging 

3import re 

4 

5from django.conf import settings 

6from django.db.models.base import ModelBase 

7from django.urls import NoReverseMatch, reverse 

8 

9from apis_core.apis_metainfo.models import RootObject 

10from apis_core.utils.settings import apis_base_uri 

11 

12NEXT_PREV = getattr(settings, "APIS_NEXT_PREV", True) 

13 

14logger = logging.getLogger(__name__) 

15 

16 

17class AbstractEntityModelBase(ModelBase): 

18 def __new__(metacls, name, bases, attrs): 

19 if name == "AbstractEntity": 

20 return super().__new__(metacls, name, bases, attrs) 

21 else: 

22 new_class = super().__new__(metacls, name, bases, attrs) 

23 if not new_class._meta.ordering: 

24 logger.warning( 

25 f"{name} inherits from AbstractEntity but does not specify 'ordering' in its Meta class. " 

26 "Empty ordering could result in inconsitent results with pagination. " 

27 "Set a ordering or inherit the Meta class from AbstractEntity.", 

28 ) 

29 

30 return new_class 

31 

32 

33class AbstractEntity(RootObject, metaclass=AbstractEntityModelBase): 

34 """ 

35 Abstract super class which encapsulates common logic between the 

36 different entity kinds and provides various methods relating to either 

37 all or one specific entity kind. 

38 

39 Most of the class methods are designed to be used in the subclass as they 

40 are considering contexts which depend on the subclass entity type. 

41 So they are to be understood in that dynamic context. 

42 """ 

43 

44 class Meta: 

45 abstract = True 

46 

47 @classmethod 

48 def get_or_create_uri(cls, uri): 

49 uri = str(uri) 

50 try: 

51 if re.match(r"^[0-9]*$", uri): 

52 p = cls.objects.get(pk=uri) 

53 else: 

54 p = cls.objects.get(uri__uri=uri) 

55 return p 

56 except Exception as e: 

57 print("Found no object corresponding to given uri." + e) 

58 return False 

59 

60 # TODO 

61 @classmethod 

62 def get_entity_list_filter(cls): 

63 return None 

64 

65 @functools.cached_property 

66 def get_prev_id(self): 

67 if NEXT_PREV: 

68 prev_instance = ( 

69 type(self) 

70 .objects.filter(id__lt=self.id) 

71 .order_by("-id") 

72 .only("id") 

73 .first() 

74 ) 

75 if prev_instance is not None: 

76 return prev_instance.id 

77 return False 

78 

79 @functools.cached_property 

80 def get_next_id(self): 

81 if NEXT_PREV: 

82 next_instance = ( 

83 type(self) 

84 .objects.filter(id__gt=self.id) 

85 .order_by("id") 

86 .only("id") 

87 .first() 

88 ) 

89 if next_instance is not None: 

90 return next_instance.id 

91 return False 

92 

93 def get_default_uri(self): 

94 try: 

95 route = reverse("GetEntityGenericRoot", kwargs={"pk": self.pk}) 

96 except NoReverseMatch: 

97 route = reverse("apis_core:GetEntityGeneric", kwargs={"pk": self.pk}) 

98 base = apis_base_uri().strip("/") 

99 return f"{base}{route}"