为什么需要这个需求?
sqlalchemy 是个好东西,虽然其文档犹如老太婆的裹脚布--又臭又长,饱受诟病
使用 restful 时sqlalchemy返回的是一个 object 类,假设前后端分离,前端无法处理
如何实现?
直接给出代码
1class Serializer(object):
2
3 def __init__(self, instance, many=False, include=[], exclude=[], depth=2):
4 self.instance = instance
5 self.many = many
6 self.include = include
7 self.exclude = exclude
8 self.depth = depth
9
10 @property
11 def data(self):
12 if self.include and self.exclude:
13 raise ValueError('include and exclude can\'t work together')
14 if self.many:
15 if isinstance(self.instance, list):
16 return self._serializerlist(self.instance, self.depth)
17 pageinfo = {
18 'items': True,
19 'pages': self.instance.pages,
20 'has_prev': self.instance.has_prev,
21 'page': self.instance.page,
22 'has_next': self.instance.has_next,
23 'iter_pages': list(self.instance.iter_pages(left_edge=1,
24 left_current=2,
25 right_current=3,
26 right_edge=1))
27 }
28 return {'data': self._serializerlist(self.instance.items,
29 self.depth),
30 'pageinfo': pageinfo}
31 return self._serializer(self.instance, self.depth)
32
33 def _serializerlist(self, instances, depth):
34 results = []
35 for instance in instances:
36 result = self._serializer(instance, depth)
37 if result:
38 results.append(result)
39 return results
40
41 def _serializer(self, instance, depth):
42 result = {}
43 if depth == 0:
44 return result
45 depth -= 1
46 model_class = self.get_model_class(instance)
47 inp = self.get_inspect(model_class)
48 model_data = self._serializer_model(inp, instance, depth)
49 relation_data = self._serializer_relation(inp, instance, depth)
50 result.update(model_data)
51 result.update(relation_data)
52 return result
53
54 def _serializer_model(self, inp, instance, depth):
55 result = {}
56 model_columns = self.get_model_columns(inp)
57 for column in model_columns:
58 result[column] = getattr(instance, column)
59 return result
60
61 def _serializer_relation(self, inp, instance, depth):
62 result = {}
63 relation_columns = self.get_relation_columns(inp)
64 for relation in relation_columns:
65 column = relation.key
66 if relation.direction in [ONETOMANY, MANYTOMANY]:
67 children = getattr(instance, column)
68 if relation.lazy == 'dynamic':
69 children = children.all()
70 result[column] = Serializer(
71 children,
72 many=True,
73 exclude=[relation.back_populates],
74 depth=depth).data
75 else:
76 child = getattr(instance, column)
77 if relation.lazy == 'dynamic':
78 child = child.first()
79 result[column] = Serializer(
80 child,
81 many=False,
82 exclude=[relation.back_populates],
83 depth=depth).data
84 return result
85
86 def get_model_class(self, instance):
87 return getattr(instance, '__class__')
88
89 def get_inspect(self, model_class):
90 return inspect(model_class)
91
92 def get_model_columns(self, inp):
93 if self.include:
94 model_columns = [
95 column.name for column in inp.columns
96 if column.name in self.include
97 ]
98 elif self.exclude:
99 model_columns = [
100 column.name for column in inp.columns
101 if column.name not in self.exclude
102 ]
103 else:
104 model_columns = [column.name for column in inp.columns]
105
106 return model_columns
107
108 def get_relation_columns(self, inp):
109 if self.include:
110 relation_columns = [
111 relation for relation in inp.relationships
112 if relation.key in self.include
113 ]
114 elif self.exclude:
115 relation_columns = [
116 relation for relation in inp.relationships
117 if relation.key not in self.exclude
118 ]
119 else:
120 relation_columns = [relation for relation in inp.relationships]
121 return relation_columns
具体使用
使用上很简单(以flask-sqlalchemy为例),原生sqlalchemy类似
多个实例
1posts = Post.query.all()
2serializer = Seralizer(posts,many=True)
3data = serializer.data
单个实例
1post = Post.query.first()
2serializer = Seralizer(post,many=False)
3data = serializer.data
排除字段
1serializer = Seralizer(post,exclude=['title'])
仅包括字段
1serializer = Seralizer(post,include=['title'])
关系查询深度
1serializer = Seralizer(post,depth=3)
-
depth 默认为2
知识共享署名-非商业性使用-相同方式共享4.0国际许可协议