Coverage for autocrud/converter.py: 63%

68 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-23 23:00 +0800

1"""數據類型轉換器模組""" 

2 

3from typing import Any, Dict, Type, get_type_hints 

4from dataclasses import dataclass, is_dataclass, fields 

5 

6# 嘗試導入 TypedDict,Python 3.8+ 可從 typing 導入 

7try: 

8 from typing import TypedDict 

9except ImportError: 

10 pass 

11 

12 

13class ModelConverter: 

14 """統一的數據模型轉換器""" 

15 

16 @staticmethod 

17 def detect_model_type(model_class: Type) -> str: 

18 """檢測模型類型""" 

19 if is_dataclass(model_class): 

20 return "dataclass" 

21 

22 # 檢查是否為 Pydantic 模型 

23 if hasattr(model_class, "__pydantic_core_schema__") or hasattr( 

24 model_class, "__fields__" 

25 ): 

26 return "pydantic" 

27 

28 # 檢查是否為 TypedDict 

29 if hasattr(model_class, "__annotations__") and hasattr( 

30 model_class, "__total__" 

31 ): 

32 return "typeddict" 

33 

34 raise ValueError(f"不支援的模型類型: {model_class}") 

35 

36 @staticmethod 

37 def extract_fields(model_class: Type) -> Dict[str, Type]: 

38 """提取模型的欄位和類型""" 

39 model_type = ModelConverter.detect_model_type(model_class) 

40 

41 if model_type == "dataclass": 

42 return {field.name: field.type for field in fields(model_class)} 

43 

44 elif model_type == "pydantic": 

45 # Pydantic v2 

46 if hasattr(model_class, "model_fields"): 

47 return { 

48 name: field.annotation 

49 for name, field in model_class.model_fields.items() 

50 } 

51 # Pydantic v1 

52 elif hasattr(model_class, "__fields__"): 

53 return { 

54 name: field.type_ for name, field in model_class.__fields__.items() 

55 } 

56 

57 elif model_type == "typeddict": 

58 return get_type_hints(model_class) 

59 

60 return {} 

61 

62 @staticmethod 

63 def to_dict(instance: Any) -> Dict[str, Any]: 

64 """將模型實例轉換為字典""" 

65 if is_dataclass(instance): 

66 from dataclasses import asdict 

67 

68 return asdict(instance) 

69 

70 # Pydantic 模型 

71 if hasattr(instance, "model_dump"): 

72 return instance.model_dump() 

73 elif hasattr(instance, "dict"): 

74 return instance.dict() 

75 

76 # TypedDict 或普通字典 

77 if isinstance(instance, dict): 

78 return instance 

79 

80 raise ValueError(f"無法轉換為字典: {type(instance)}") 

81 

82 @staticmethod 

83 def from_dict(model_class: Type, data: Dict[str, Any]) -> Any: 

84 """從字典創建模型實例""" 

85 model_type = ModelConverter.detect_model_type(model_class) 

86 

87 if model_type == "dataclass": 

88 return model_class(**data) 

89 

90 elif model_type == "pydantic": 

91 return model_class(**data) 

92 

93 elif model_type == "typeddict": 

94 # TypedDict 只是類型提示,實際上還是字典 

95 return data 

96 

97 raise ValueError(f"無法從字典創建實例: {model_class}") 

98 

99 

100# 使用範例 

101if __name__ == "__main__": 

102 from dataclasses import dataclass 

103 from typing import Dict 

104 

105 @dataclass 

106 class User: 

107 name: str 

108 age: int 

109 email: str 

110 

111 # 測試轉換器 

112 converter = ModelConverter() 

113 

114 # 檢測類型 

115 print(f"模型類型: {converter.detect_model_type(User)}") 

116 

117 # 提取欄位 

118 fields_info = converter.extract_fields(User) 

119 print(f"欄位信息: {fields_info}") 

120 

121 # 創建實例並轉換 

122 user = User(name="Alice", age=30, email="alice@example.com") 

123 user_dict = converter.to_dict(user) 

124 print(f"轉為字典: {user_dict}") 

125 

126 # 從字典創建實例 

127 new_user = converter.from_dict(User, user_dict) 

128 print(f"從字典創建: {new_user}")