慕课网Flask构建可扩展的RESTful API-6. 模型对象的序列化

6. 模型对象的序列化

1.理解序列化时的default函数

我们最想做的一件事情,就是在视图函数中,读取出模型之后,还要把他的属性读出来,转换成一个字典。我们想直接jsonfiy(user)

现在jsonfiy并不能直接序列化对象,所以我们的目标就是必须想办法让jsonfiy直接序列化对象。

jsonfiy在序列化对象的时候,如果不知道如何序列化当前传进来的参数,就会去调用JSONEncoder类的default函数。

def default(self, o):
"""Implement this method in a subclass such that it returns a
serializable object for ``o``, or calls the base implementation (to
raise a :exc:`TypeError`).

For example, to support arbitrary iterators, you could implement
default like this::

def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
return JSONEncoder.default(self, o)
"""
if isinstance(o, datetime):
return http_date(o.utctimetuple())
if isinstance(o, date):
return http_date(o.timetuple())
if isinstance(o, uuid.UUID):
return str(o)
if hasattr(o, '__html__'):
return text_type(o.__html__())
return _json.JSONEncoder.default(self, o)

目前的default是没有提供对对象的序列化的,所以我们这里最关键的就是要重写default方法。在重写的过程中实现对对象的序列化就可以了

2.不完美的对象转字典

我们首先要做到的就是让Flask可以调用到我们自己定义的default函数。要做到这一点,我们需要继承JSONEncoder,然后重写defualt方法,然后继承Flask,在子类里,替换掉Flask原有的json_encoder对象。然后,是实例化Flask核心对象的时候,使用我们的子类进行实例化

class JSONEncoder(_JSONEncoder):

def default(self, o):
# 只能转换实例变量
return o.__dect__


class Flask(_Flask):
json_encoder = JSONEncoder()

上面的写法o.__dect__只能转换实例变量,不能讲类变量也转换成字典。

3.深入理解dict机制

在Python中创建一个dict有很多种方式:

  1. 直接定义一个字典
r = {
'name': 'gwf'
}
  1. 使用dict函数
r = dict(name='gwf')
  1. 将一个对象传入dict函数 值得研究的是这第三种方法,当将一个对象传入dict函数的时候,他会去调用keys函数

image.png

keys 方法的目的就是为了拿到字典里所有的键,至于说这些键有那么,完全有我们自己来定义。keys 返回的必须是一个元组或者列表来声明要序列化的键。

而dict会以中括号的形式来拿到对应的值,如o["name"],但是默认是不能这么访问的,我们需要编写__getitem__函数

class Person:
name = 'gwf'
age = 18

def __init__(self):
self.gender = 'male'

def keys(self):
return ('name', 'age', 'gender')

def __getitem__(self, item):
return getattr(self, item)


o = Person()
print(dict(o))
# {'name': 'gwf', 'age': 18, 'gender': 'male'}

这样我们就成功的讲一个对象转化成了字典的形式,并且无论类变量和实例变量,都可以转化,更加灵活的是,我们可以自己控制,那些变量需要转化,哪些变量不需要转化

注意: 如果我们只想序列化一个元素

def keys(self):
return ('name')

这样是不行的,因为只有一个元素的元素不是这样定义的,我们需要在后面加上一个逗号

def keys(self):
return ('name',)

4.序列化SQLALChemy模型

有了之前的基础,我们就知道怎么序列化user对象了,我们只需要在User类中定义keys和getitem方法,然后在default函数中使用dict()函数即可

class JSONEncoder(_JSONEncoder):

def default(self, o):
return dict(o)


class Flask(_Flask):
json_encoder = JSONEncoder

models/user.py

class User(Base):
id = Column(Integer, primary_key=True)
email = Column(String(50), unique=True, nullable=False)
auth = Column(SmallInteger, default=1)
nickname = Column(String(24), nullable=False)
_password = Column('password', String(128))

# SQLALChemy的实例化是不会调用__init__函数的,要想让他调用就需要
# @orm.reconstructor这个装饰器
@orm.reconstructor
def __init__(self):
self.fields = ['id', 'email', 'nickname']

def keys(self):
return self.fields

# 支持隐藏字段
def hide(self, *keys):
[self.fields.remove(key) for key in keys]
# 支持添加字段
def append(self, *keys):
[self.fields.append(key) for key in keys]

5.完善序列化

优化1:每一个模型如果需要序列化,都要有getitem方法,可以放到基类里面去

优化2:default函数,是递归调用的,只要遇到不能序列化的对象,就会调用default函数。所以如果有其他类型,我们需要修改完善我们的default函数

优化3:我们的default函数需要增加容错性

class JSONEncoder(_JSONEncoder):

def default(self, o):
if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
return dict(o)
# 兼容其他的序列化
if isinstance(o, date):
return o.strftime('%Y-%m-%d')
raise ServerError()

优化4:之前编写的新的Flask类,JsonEncoder类都是不会轻易改变的,但是app.py中的一些其他方法,却是 经常改变的,应该把他们放在init文件中

6.ViewModel对于API有意义吗?

viewmodel对于API来说,特别是内部开发来说非常有意义

viewmodel是为了我们的视图层,提供个性化的试图模型。SQLALChemy返回的模型是原始模型(格式和数据库中存储的一模一样)。 而前端可能需要我们返回一个意义更加明确的字段。

原始模型是根据数据库来生成的,他的格式是一定的,但是我们在视图层中或者API的返回中,要根据业务去具体的个性化一个个属性的 格式,这就必然存在一个由原始模型向视图模型转换的过程,这个过程最适合的是在View_model中进行一个转换。

我们在视图层写转换的代码,一来会污染视图层的代码,二来会难以复用 并且有的试图模型可能会比较复杂,设计到多个原始模型,这个代码必定会比较复杂,写在视图函数中就会非常不合适

对于完全严格意义上的RESTFul,viewmodel的意义并不大,因为完全资源意义的RESTFul是不考虑业务逻辑的

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ACM算法日常

UVA11988:悲剧文本(模拟链表)

You’re typing a long text with a broken keyboard. Well it’s not so badly broken....

10910
来自专栏小狼的世界

Ruby学习笔记

Ruby语言中,以对象为基本单位,可以说所有的元素都是对象。按照之前对于面向对象程序的理解,对象是指包含了特定属性和方法集合的一组程序。对象由类来定义,具体的表...

9720
来自专栏海天一树

图的广度优先搜索

广度优先搜索算法是最简便的图的搜索算法之一,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索...

16230
来自专栏用户画像

7.7.1外部排序

内部排序都是在内存中进行的,而在实际应用中,经常需要对大文件进行排序,因为文件中的记录很多、信号量庞大,无法将整个文件拷贝进内存中进行排序。因此,需要将待排序的...

9110
来自专栏小灰灰

ForkJoin 学习使用笔记

ForkJoin 学习使用笔记 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结...

329100
来自专栏java架构师

【SQL Server】系统学习之三:逻辑查询处理阶段-六段式

一、From阶段 针对连接说明: 1、笛卡尔积 2、on筛选器 插播:unknown=not unknuwn 缺失的值; 筛选器(on where having...

372110
来自专栏人工智能LeadAI

共享变量 tensorflow解读

你可以在怎么使用变量中所描述的方式来创建,初始化,保存及加载单一的变量.但是当创建复杂的模块时,通常你需要共享大量变量集并且如果你还想在同一个地方初始化这所有的...

13420
来自专栏尾尾部落

[剑指offer] 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为...

10820
来自专栏代码世界

Python之面向对象四

面向对象进阶 一、关于面向对象的两个内置函数 isinstance   判断类与对象的关系    isinstance(obj,cls)检查obj是否是类 cl...

400130
来自专栏听雨堂

Python学习笔记(3):数据集操作-列的统一操作

对数据库查询,将得到一个数据集: rs=AccessDB.GetData("select * from log where f_code='600259' ...

26760

扫码关注云+社区

领取腾讯云代金券