sqlalchemy序列化为json
为什么需要这个需求?
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
