为什么需要这个需求?
sqlalchemy 是个好东西,虽然其文档犹如/老太婆的裹脚布--又臭又长/,饱受诟病
使用 restful 时sqlalchemy返回的是一个 object 类,假设前后端分离,前端无法处理
如何实现?
直接给出代码
class Serializer(object): def __init__(self, instance, many=False, include=[], exclude=[], depth=2): self.instance = instance self.many = many self.include = include self.exclude = exclude self.depth = depth @property def data(self): if self.include and self.exclude: raise ValueError('include and exclude can\'t work together') if self.many: if isinstance(self.instance, list): return self._serializerlist(self.instance, self.depth) pageinfo = { 'items': True, 'pages': self.instance.pages, 'has_prev': self.instance.has_prev, 'page': self.instance.page, 'has_next': self.instance.has_next, 'iter_pages': list(self.instance.iter_pages(left_edge=1, left_current=2, right_current=3, right_edge=1)) } return {'data': self._serializerlist(self.instance.items, self.depth), 'pageinfo': pageinfo} return self._serializer(self.instance, self.depth) def _serializerlist(self, instances, depth): results = [] for instance in instances: result = self._serializer(instance, depth) if result: results.append(result) return results def _serializer(self, instance, depth): result = {} if depth == 0: return result depth -= 1 model_class = self.get_model_class(instance) inp = self.get_inspect(model_class) model_data = self._serializer_model(inp, instance, depth) relation_data = self._serializer_relation(inp, instance, depth) result.update(model_data) result.update(relation_data) return result def _serializer_model(self, inp, instance, depth): result = {} model_columns = self.get_model_columns(inp) for column in model_columns: result[column] = getattr(instance, column) return result def _serializer_relation(self, inp, instance, depth): result = {} relation_columns = self.get_relation_columns(inp) for relation in relation_columns: column = relation.key if relation.direction in [ONETOMANY, MANYTOMANY]: children = getattr(instance, column) if relation.lazy == 'dynamic': children = children.all() result[column] = Serializer( children, many=True, exclude=[relation.back_populates], depth=depth).data else: child = getattr(instance, column) if relation.lazy == 'dynamic': child = child.first() result[column] = Serializer( child, many=False, exclude=[relation.back_populates], depth=depth).data return result def get_model_class(self, instance): return getattr(instance, '__class__') def get_inspect(self, model_class): return inspect(model_class) def get_model_columns(self, inp): if self.include: model_columns = [ column.name for column in inp.columns if column.name in self.include ] elif self.exclude: model_columns = [ column.name for column in inp.columns if column.name not in self.exclude ] else: model_columns = [column.name for column in inp.columns] return model_columns def get_relation_columns(self, inp): if self.include: relation_columns = [ relation for relation in inp.relationships if relation.key in self.include ] elif self.exclude: relation_columns = [ relation for relation in inp.relationships if relation.key not in self.exclude ] else: relation_columns = [relation for relation in inp.relationships] return relation_columns
具体使用
使用上很简单(以flask-sqlalchemy为例),原生sqlalchemy类似
多个实例
posts = Post.query.all() serializer = Seralizer(posts,many=True) data = serializer.data
单个实例
post = Post.query.first() serializer = Seralizer(post,many=False) data = serializer.data
排除字段
serializer = Seralizer(post,exclude=['title'])
仅包括字段
serializer = Seralizer(post,include=['title'])
关系查询深度
serializer = Seralizer(post,depth=3)
- depth
默认为2