简体   繁体   English

如何从 Flask-SQLAlchemy 关系查询返回序列化的 JSON?

[英]How to return serialized JSON from Flask-SQLAlchemy relationship query?

i'm using Flask-SQLAlchemy and i have the following models with one to many relationship,我正在使用 Flask-SQLAlchemy 并且我有以下具有一对多关系的模型,

class User(db.Model):

    # Table name
    __tablename__ = "users"

    # Primary key
    user_id = db.Column(db.Integer, primary_key=True)

    # Fields (A-Z)
    email = db.Column(db.String(50), nullable=False, unique=True)
    password = db.Column(db.String, nullable=False)
    username = db.Column(db.String(50), unique=True)

    # Relationships (A-Z)
    uploads = db.relationship("Upload", backref="user")

class Upload(db.Model):

    # Table name
    __tablename__ = "uploads"

    # Primary key
    upload_id = db.Column(db.Integer, primary_key=True)

    # Fields (A-Z)
    name = db.Column(db.String(50), nullable=False)
    path_to_file = db.Column(db.String(256), nullable=False, unique=True)
    uploaded_by = db.Column(db.Integer, db.ForeignKey("users.user_id"))

and i want to return JSON like this:我想像这样返回 JSON:

{
    "users": [
        {
            "email": "vargovcik.marek@gmail.com",
            "uploads": [
                {
                    "name": "1.png",
                    "path_to_file": "static/1.png"
                }
            ],
            "username": "maro"
        },
        {
            "email": "makos@gmail.com",
            "uploads": [
                {
                    "name": "2.jpg",
                    "path_to_file": "static/2.jpg"
                }
            ],
            "username": "makos"
        }
    ]
}

So basically i want to return user object with all uploads (files user uploaded) in array.所以基本上我想返回用户 object 以及数组中的所有上传(用户上传的文件)。

I know i can access Upload class object within user with User.uploads (created with db.relationship) but i need some kind of serializer.我知道我可以使用 User.uploads(使用 db.relationship 创建)在用户中访问 Upload class object,但我需要某种序列化程序。

I wanted to add custom serialize() method to all my models:我想为我的所有模型添加自定义 serialize() 方法:

# User serializer
    def serialize_user(self):
        if self.uploads:
            uploads = [upload.serialize_upload() for upload in self.uploads]
        return {
            "email": self.email,
            "password": self.password,
            "username": self.username,
            "uploads": uploads
        }

# Upload serializer
    def serialize_upload(self):
        if self.user:
            dict_user = self.user.serialize_user()
        return {
            "name": self.name,
            "path_to_file": self.path_to_file,
            "user": dict_user
        }

But problem with this is that i end up with nesting loop.但问题是我最终得到了嵌套循环。 My User object has upload files and each upload has it's user's data and these user's data has uploads files...我的用户 object 有上传文件,每次上传都有它的用户数据,这些用户数据有上传文件......

My view endpoint:我的视图端点:

@app.route('/users', methods=["GET"])
def get_users():
    users = [user.serialize_user() for user in User.query.all()]
    return jsonify(users)

Error:错误:

RecursionError: maximum recursion depth exceeded while calling a Python object

Partial solution:部分解决方案:

I can simply ommit serializing user object inside Upload serializer but then i won't be able to create similiar endpoint but to get uploads.我可以简单地忽略在上传序列化程序中序列化用户 object,但是我将无法创建类似的端点,只能获取上传。 Example: /uploads - JSON with all uploads and user object nested.示例:/uploads - JSON 嵌套了所有上传和用户 object。

How can i effectively work with relationships to return them as serialized JSON data similiar to JSON structure above?我如何有效地处理关系以将它们返回为类似于上面的 JSON 结构的序列化 JSON 数据?

As you said, you can simply write a second serializer method. 如您所说,您可以简单地编写第二个serializer方法。 So you keep the other one for your /uploads API call. 因此,您可以保留另一个用于/ uploads API调用。

# User serializer
def serialize_user(self):
    if self.uploads:
        uploads = [upload.serialize_upload_bis() for upload in self.uploads]
    return {
            "email": self.email,
            "password": self.password,
            "username": self.username,
            "uploads": uploads
    }

# Upload serializer
def serialize_upload_bis(self):
    return {
        "name": self.name,
        "path_to_file": self.path_to_file,
    }

def serialize_upload(self):
    if self.user:
        dict_user = self.user.serialize_user()
    return {
        "name": self.name,
        "path_to_file": self.path_to_file,
        "user": dict_user
    }

Although the question is quite old, I want to add a more generic answer, because I also was faced with this issue.虽然这个问题很老,但我想添加一个更通用的答案,因为我也遇到过这个问题。 The following serializer works for all classes with relationships:以下序列化程序适用于所有具有关系的类:

from sqlalchemy.orm.collections import InstrumentedList
class Serializer(object):

    def serialize(self):
        serializedObject= {}
        for c in inspect(self).attrs.keys():
            attribute = getattr(self, c)
            if(type(attribute) is InstrumentedList ):
                serializedObject[c]= Component.serialize_list(attribute)
            else:
                serializedObject[c]= attribute                
        return serializedObject

    @staticmethod
    def serialize_list(l):
        return [m.serialize() for m in l]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM