Coverage for src / lexigram / admin / ui / columns / column / base.py: 88%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-11 17:07 +0800

1""" 

2Base Column class with core functionality. 

3""" 

4 

5from __future__ import annotations 

6 

7from abc import ABC, abstractmethod 

8from typing import TYPE_CHECKING, Any 

9 

10if TYPE_CHECKING: 

11 from collections.abc import Callable 

12 

13 

14class Column(ABC): 

15 """Base class for all table columns with fluent API. 

16 

17 This class implements the Builder pattern, allowing configuration 

18 through method chaining. All configuration methods return `self` 

19 to enable fluent syntax. 

20 

21 Attributes: 

22 name: Column field name (supports nested fields like "user.name") 

23 label: Display label for column header 

24 

25 Example: 

26 >>> column = TextColumn("name").sortable().searchable() 

27 >>> column = DateColumn("created_at").datetime().relative() 

28 """ 

29 

30 def __init__(self, name: str, label: str | None = None): 

31 """ 

32 Initialize a column. 

33 

34 Args: 

35 name: Column field name (database column). Supports nested 

36 fields using dot notation (e.g., "user.email") 

37 label: Display label (defaults to title-cased name) 

38 """ 

39 self.name = name 

40 self.label = label or name.replace("_", " ").title() 

41 self._sortable = False 

42 self._searchable = False 

43 self._toggleable = True 

44 self._copyable = False 

45 self._filterable = False 

46 self._filter_instance = None # Store Filter instance 

47 self._exportable = True 

48 self._limit = None 

49 self._wrap = False 

50 self._alignment = "left" 

51 self._width: int | None = None 

52 self._tooltip: str | None = None 

53 self._format_callback: Callable | None = None 

54 self._visible = True 

55 self._visible_callback: Callable | None = None 

56 self._visibility_classes: list[str] = [] 

57 self._pinned: str | None = None # 'left' or 'right' 

58 self._masker: Callable[[Any], str] | None = None 

59 

60 def get_value(self, record: dict) -> Any: 

61 """Extract value from record.""" 

62 # Support nested keys like "user.name" 

63 keys = self.name.split(".") 

64 value = record 

65 for key in keys: 

66 if isinstance(value, dict): 

67 value = value.get(key) # type: ignore[assignment] 

68 else: 

69 value = getattr(value, key, None) 

70 if value is None: 

71 break 

72 return value 

73 

74 def is_searchable(self) -> bool: 

75 """Check if this column is searchable.""" 

76 return self._searchable 

77 

78 def is_sortable(self) -> bool: 

79 """Check if this column is sortable.""" 

80 return self._sortable 

81 

82 def get_filter_instance(self) -> Any: 

83 """Get the filter instance associated with this column.""" 

84 return self._filter_instance 

85 

86 def format_value(self, value: Any) -> Any: 

87 """ 

88 Format the value using the configured format callback. 

89 

90 Args: 

91 value: The raw value from the record 

92 

93 Returns: 

94 Formatted value 

95 """ 

96 if self._format_callback: 

97 return self._format_callback(value) 

98 return value 

99 

100 @abstractmethod 

101 def render(self, value: Any, record: dict) -> Any: 

102 """ 

103 Render the column value as HTML. 

104 """ 

105 

106 

107AbstractColumn = Column