前言
sqlalchemy虽然说是python最好用的orm之一,但是其内部的功能还是有不完善的地方,就比如序列化,如果表字段要破百,假设前后端不分离的情况下还好,但是前后端分离的情况下,前端无法处理,而sqlalchemy又不带序列化,所以我们只能自己写一个序列化了
代码
from sqlalchemy import inspect
from sqlalchemy.orm import ONETOMANY, MANYTOMANY
class Serializer:
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)
- Post link: https://www.godhearing.cn/sqlalchemy-xu-lie-hua/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.