首页 > 解决方案 > Flask Sqlalchemy 模型通过调用 model.__dict__ 返回不同的结果(有时显示反向引用,有时不显示)

问题描述

我想从查询中获取结果并将其发送jsonify给客户端,我创建了一个获取模型实例的函数,使用它的“ dict ”属性删除未使用的值'_sa_instance_state',然后将新字典作为 jsonified 对象返回给客户端。这是我的代码:

def display(Object):
        data= Object.__dict__.copy()
        del data['_sa_instance_state']
        return data`

user = User.query.first()

@app.route('/')
def main():
       return jsonify(display(user))

有时我会收到此错误

TypeError:Admin 类型的对象不是 JSON 可序列化的

其中 admin 是另一个model在 User 模型中有一个 backref 的地方。这里发生了奇怪的事情,因为有时没有改变任何东西我没有得到错误。当我试图找出 jupyter 笔记本中发生的事情时,我发现__dict__它没有显示 backrefs,jupyter notebook但有时当我从烧瓶应用程序调用它时它会显示 backrefs。

标签: pythonjsonflasksqlalchemyflask-sqlalchemy

解决方案


不要__dict__在 SQLAlchemy 模型上使用。是的,可能存在反向引用,具体取决于您配置引用加载的方式,其他 SQLAlchemy 特定信息也是如此。

您可以使用运行时自省来获取InstanceState实例。然后,您可以决定在 JSON 中反映什么以及应该忽略什么:

from operator import attrgetter
from sqlalchemy import inspect

def display(object, only_loaded=False):
    insp = inspect(object)
    assert insp.is_instance
    value = attrgetter('loaded_value' if only_loaded else 'value')
    return {
        name: value(attr) for name, attr in insp.attrs.items()
        if name not in insp.mapper.relationships
    }

以上使用Mapper.relationships命名空间跳过任何相关对象。处理相关对象的另一种方法是递归地将它们转换为字典:

def display(object, include_related=True, only_loaded=False, seen=None):
    insp = inspect(object)
    assert insp.is_instance
    value = attrgetter('loaded_value' if only_loaded else 'value')
    result = {
        name: value(attr) for name, attr in insp.attrs.items()
        if name not in insp.mapper.relationships
    }
    if include_related:
        if seen is None:
            seen = set()
        seen.add(id(object))
        for name, rel in insp.mapper.relationships.items():
            related = value(insp.attrs[name])
            if related is not None:
                if rel.uselist:
                    displays = result[name] = []
                    for r in related:
                        if id(r) in seen:
                            continue
                        displays.append(display(r, True, only_loaded, seen))
                else:
                    if id(related) in seen:
                        continue
                    result[name] = display(related, True, only_loaded)
    return result

但请考虑到,使用双向关系对象,您最终可能会很快反映比您预期的更大的数据库块。

as_dict()就个人而言,我发现向我的模型添加一个方法来生成要反射的字典,或者使用提供反射 API的 Flask 插件更容易。


推荐阅读