6. Serialization of model objects

1. Understand the default function for serialization

One of the things we really want to do is read the model in view functions, and then read out its properties and turn them into a dictionary. We want to directly jsonfiy(user)

Now jsonfiy can’t serialize objects directly, so our goal is to have jsonfiy serialize objects directly.

Jsonfiy calls the default function of the JSONEncoder class if it does not know how to serialize the currently passed parameters.

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)
Copy the code

Currently, default does not provide serialization of objects, so the key here is to rewrite the default method. Serialize the object during the rewrite process

2. Turn imperfect objects to dictionaries

The first thing we need to do is give Flask the ability to call our own default function. To do this, we need to inherit the JSONEncoder, override the Defualt method, inherit Flask, and, in subclasses, replace the original Flask json_encoder object. Then, it’s time to instantiate the Flask core object, using our subclass

class JSONEncoder(_JSONEncoder):

def default(self, o):
Only instance variables can be converted
return o.__dect__


class Flask(_Flask):
json_encoder = JSONEncoder()
Copy the code

__dect__ can only convert instance variables, not class variables to dictionaries.

3. Understand dict mechanics in depth

There are several ways to create a dict in Python:

  1. Define a dictionary directly
r = {
'name': 'gwf'
}
Copy the code
  1. Using dict functions
r = dict(name='gwf')
Copy the code
  1. Passing an object to dict is worth investigating as the third method. When passing an object to dict, it calls keys

The purpose of the keys method is to get all the keys in the dictionary, and the keys are up to us. Keys must return a tuple or list of keys to serialize.

Dict retrieves values in brackets, such as o[“name”], but this is not the default, so we need to write __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'}
Copy the code

So we’ve successfully converted an object into a dictionary, and we can convert both class variables and instance variables, and even more flexibly, we can control which variables need to be converted and which ones don’t

Note: If we want to serialize only one element

def keys(self):
return ('name')
Copy the code

That doesn’t work, because an element with only one element is not defined this way, so we need to put a comma after it

def keys(self):
return ('name'.)Copy the code

Serialize the SQLALChemy model

Now that we know how to serialize the user object, we just define the keys and getitem methods in the User class, and then use the dict() function in the default function

class JSONEncoder(_JSONEncoder):

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


class Flask(_Flask):
json_encoder = JSONEncoder
Copy the code

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))

An instantiation of # SQLALChemy will not call __init__, it will
# @orm.reconstructor this decorator
@orm.reconstructor
def __init__(self):
self.fields = ['id'.'email'.'nickname']

def keys(self):
return self.fields

# Support hidden fields
def hide(self, *keys):
[self.fields.remove(key) for key in keys]
# Support adding fields
def append(self, *keys):
[self.fields.append(key) for key in keys]
Copy the code

5. Perfect serialization

Optimization 1: Every model that needs serialization should have a getitem method that can be placed in the base class

Optimization 2: The default function, which is called recursively, will be called whenever it encounters an object that cannot be serialized. So if there are other types, we need to modify our default function

Optimization 3: Our default function needs to increase fault tolerance

class JSONEncoder(_JSONEncoder):

def default(self, o):
if hasattr(o, 'keys') and hasattr(o, '__getitem__') :return dict(o)
Compatible with other serializations
if isinstance(o, date):
return o.strftime('%Y-%m-%d')
raise ServerError()
Copy the code

Optimism4: The new Flask classes, JsonEncoder classes written before are not easily changed, but some of the other methods in app.py are always changed and should be put in an init file

6. Does the ViewModel make sense to the API?

Viewmodels make a lot of sense for apis, especially for internal development

The ViewModel is a personalized attempt model for our view layer. The model returned by SQLALChemy is the original model (in the same format as the one stored in the database). The front end might require us to return a field with a more explicit meaning.

Original model is based on the database to generate, his format is certain, but we are in the view layer or API returns, according to the business to specific personalized format of each attribute, it must exist a from the original model to the view model transformation process, this process is the most suitable for a transformation in the View_model.

When we write the transformation code in the view layer, on the one hand, it will pollute the code in the view layer, and on the other hand, it will be difficult to reuse and some of the attempted models may be complicated. When we design multiple original models, the code will be complicated, and it will be very inappropriate to write in the view function

The ViewModel doesn’t make a lot of sense for full, strictly RESTFul, because full resource RESTFul doesn’t care about business logic