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

作者: honmaple
链接: https://honmaple.me/articles/2016/12/sqlalchemy序列化为json.html
版权: CC BY-NC-SA 4.0 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat
alipay

加载评论