Coverage for src / lexigram / admin / relations / manager.py: 62%

29 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-13 22:14 +0800

1"""Relation manager for lexigram-admin. 

2 

3Provides base class for managing entity relationships in admin views. 

4""" 

5 

6from __future__ import annotations 

7 

8from abc import ABC, abstractmethod 

9from typing import TYPE_CHECKING, Any, ClassVar 

10 

11if TYPE_CHECKING: 

12 from lexigram.ui.columns.types import Column 

13from lexigram.di.decorators import inject 

14 

15 

16@inject 

17class AbstractRelationManager(ABC): 

18 """Base class for managing entity relationships. 

19 

20 Subclasses define how to display and query related entities 

21 for a parent resource. 

22 

23 Example: 

24 class UserPetsRelationManager(AbstractRelationManager): 

25 relationship_name = "pets" 

26 

27 @classmethod 

28 def table(cls, table_config=None): 

29 return [ 

30 TextColumn("name").sortable(), 

31 BadgeColumn("species"), 

32 ] 

33 

34 async def get_query(self): 

35 return await pet_service.list(user_id=self.parent_id) 

36 """ 

37 

38 relationship_name: ClassVar[str] = "" 

39 

40 def __init__(self, parent_id: Any = None, parent: Any = None): 

41 """Initialize relation manager. 

42 

43 Args: 

44 parent_id: ID of the parent entity 

45 parent: The parent entity object (optional) 

46 """ 

47 self.parent_id = parent_id 

48 self.parent = parent 

49 

50 @classmethod 

51 @abstractmethod 

52 def table(cls, table_config: Any = None) -> list[Column]: 

53 """Define columns for displaying related entities. 

54 

55 Args: 

56 table_config: Optional table configuration 

57 

58 Returns: 

59 List of Column instances 

60 """ 

61 ... 

62 

63 @abstractmethod 

64 async def get_query(self) -> list[Any]: 

65 """Get related entities for the parent. 

66 

67 Returns: 

68 List of related entities 

69 """ 

70 ... 

71 

72 async def count(self) -> int: 

73 """Count related entities. 

74 

75 Returns: 

76 Number of related entities 

77 """ 

78 items = await self.get_query() 

79 return len(items) if items else 0 

80 

81 async def get_items( 

82 self, 

83 *, 

84 page: int = 1, 

85 per_page: int = 20, 

86 **filters: Any, 

87 ) -> list[Any]: 

88 """Get paginated related entities. 

89 

90 Args: 

91 page: Page number (1-indexed) 

92 per_page: Items per page 

93 **filters: Additional filters 

94 

95 Returns: 

96 List of related entities for the page 

97 """ 

98 items = await self.get_query() 

99 if not items: 

100 return [] 

101 

102 start = (page - 1) * per_page 

103 end = start + per_page 

104 return items[start:end] 

105 

106 @classmethod 

107 def get_relationship_name(cls) -> str: 

108 """Get the name of this relationship.""" 

109 return cls.relationship_name or cls.__name__.lower().replace( 

110 "relationmanager", 

111 "", 

112 ) 

113 

114 

115__all__ = ["AbstractRelationManager"]