Web API 如何在返回的 JSON 响应中加入来自多个数据模型类的字段(自定义响应模式)

web-api
flask-sqlalchemy

#1

我现在需要查询“图片”表的数据并获取下来,转为json,然后我还需要将对应图片的评论,从“评论”表获取下来,合并为一个json,这样的逻辑合理吗,弄了很久都没弄好,因为我评论和图片是放两个表,然后首页需要两个表的数据,但是弄了好久还是没能实现。求求各位来帮帮我


#3

如果你没有用 Marshmallow 之类的工具,那么最简单的方法就是定义一个模式函数,接受这两个表的数据作为参数,在函数内把数据组合成一个字典:

def index_page_schema(photo, comments):
    return {
        'filename': photo.filename,
        'author': photo.author,
        'comments': [comment.body for comment in comments]  # 这里你大概会创建另一个评论模式,在这里用 for 循环迭代
    }

# 视图函数大概这样
@app.route('/')
def index():
    photo = Photo.query_from_database()
    comments = Comment.query_from_database()
    data = index_page_schema(photo, comments)
    return jsonify(data)

#4

我现在逻辑是这样的,我首页需要显示最新十张图片信息和评论,我评论和图片分开两个表,然后根据图片查询图片对应的评论,我这样查询对吗?

class Comment(db.Model):
    '''
    评论表
    '''
    __tablename__ = 'comment'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    content = db.Column(db.String(1024))
    image_id = db.Column(db.Integer, db.ForeignKey('image.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    status = db.Column(db.Integer, default=0) #0 正常 1被删除
    user = db.relationship('Image')

    def __init__(self, content, image_id, user_id):
        self.content = content
        self.image_id = image_id
        self.user_id = user_id

    def to_json(self):
        dict = self.__dict__
        if "_sa_instance_state" in dict:
            del dict["_sa_instance_state"]
        return dict
class Image(db.Model):
    '''
    图片
    '''
    __tablename__='image'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    url = db.Column(db.String(512))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    created_date = db.Column(db.DateTime)
    likecount = db.Column(db.Integer)
    comments = db.relationship('Comment')

    def __init__(self, url, user_id):
        self.url = url
        self.user_id = user_id
        self.created_date = datetime.now()
        self.likecount = 0
        self.dislikecount = 0

    def to_json(self):
        dict = self.__dict__
        if "_sa_instance_state" in dict:
            del dict["_sa_instance_state"]
        return dict

这两个表是图片和评论。
查询语句这样,

images = Image.query.order_by(Image.id.desc()).limit(10).all()

#5

查询语句最后的 .all() 似乎不需要?

你一开始的描述没有说清楚,如果是多个图片和对应的评论,而且有外键的话,上面我写的那个函数需要相应调整下。


#6

不好意思,我昨晚半夜脑袋不清醒,所以描述不清,这个问题困扰了很久


#7

我现在写的这个,

@app.route('/', methods={'post','get'})
def index():
    images = Image.query.order_by(Image.id.desc()).limit(10)
    images = images
    comment_list = []
    for image in images:
        comment_list.append(image.to_json())
        # for comment in image.comments:
            # comment_list.append(comment.to_json())
    return jsonify(comment_list)

返回的json是这样的,这个是返回了图片的json

 {
    "created_date": "Sun, 26 Apr 2020 13:49:25 GMT", 
    "id": 500, 
    "likecount": 0, 
    "url": "http://images.nowcoder.com/head/852m.png", 
    "user_id": 100
  }, 
  {
    "created_date": "Sun, 26 Apr 2020 13:49:25 GMT", 
    "id": 499, 
    "likecount": 0, 
    "url": "http://images.nowcoder.com/head/452m.png", 
    "user_id": 100
  }, 
往下还有8个···

这个是返回了评论的json,

{
    "content": "this is a comment0", 
    "id": 2496, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 
  {
    "content": "this is a comment1", 
    "id": 2497, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 
  {
    "content": "this is a comment2", 
    "id": 2498, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 
  {
    "content": "this is a comment3", 
    "id": 2499, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 
  {
    "content": "this is a comment4", 
    "id": 2500, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 
···往下还有5个评论。

我现在期望的结果是要这样的:

 {
    "created_date": "Sun, 26 Apr 2020 13:49:25 GMT", 
    "id": 500, 
    "likecount": 0, 
    "url": "http://images.nowcoder.com/head/852m.png", 
    "user_id": 100

    "content": "this is a comment0",  "this is a comment1",  "this is a comment2", 
    "id": 2496, 
    "image_id": 500, 
    "status": 0, 
    "user_id": 100
  }, 

目前两个json只能单独返回,不太清楚两个json要怎么一起返回,我想要的效果是:图片和图片对应的多条评论都能显示在前端,希望都能用json格式返回,因为我在写前端的时候,我需要图片和图片对应的多条评论一起显示,这个点我不知道要怎么解决,或者有没有更好的办法解决这个问题。PS:我前端用的是uniapp。麻烦grey了!希望能帮帮我,非常感谢您!


#8

我前面给出的代码示例你好像没有看?基本还是按照那个代码示例的思路,下面根据你补充的代码细节做一些修改:

def image_schema(image):
    # 这里只列出来三个字段,其他的你自己补充
    return {
        'id': image.id,
        'url': image.url,
        'comments': [comment.content for comment in image.comments]
    }


@app.route('/', methods={'post','get'})
def index():
    images = Image.query.order_by(Image.id.desc()).limit(10)
    data = []
    for image in images:
        data.append(image_schema(image))
    # 或者上面三行也可以简写为 data = [image_schema(image) for image in images]
    return jsonify(data)

#9

非常感谢greyli的帮助,之前您给我的代码已经看了。现在遇到一个新问题:sob:,我现在需要加上评论对应的用户,期望的json如下:(顺便问一下这样的json合理吗)

{
  "comment_list": [
    {
      "content": "this is a comment0", 
      "username": "User98"
    }, 
    {
      "content": "this is a comment1", 
      "username": "User98"
    }, 
    {
      "content": "this is a comment2", 
      "username": "User98"
    }, 
    {
      "content": "this is a comment3", 
      "username": "User98"
    }, 
    {
      "content": "this is a comment4", 
      "username": "User98"
    }
  ], 
  "created_date": "Tue, 28 Apr 2020 00:10:06 GMT", 
  "id": 491, 
  "image_userid": 99, 
  "likecoun": 0, 
  "url": "http://images.nowcoder.com/head/691m.png"
}

我现在单个这样的json格式已经实现了,但是我在循环这部分可能出了问题,导致我现在只能循环到一张图片的信息,麻烦greyli帮我看看,或者有没有更好的办法。麻烦greyli了!非常感谢您的耐心指导。
代码如下:
用户表:

class User(db.Model):
    __tablename='user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(80), unique=True)
    password = db.Column(db.String(255))
    salt = db.Column(db.String(32))
    head_url = db.Column(db.String(256))
    images = db.relationship('Image', backref='user', lazy='dynamic')

这个视图函数:

@app.route('/', methods={'post','get'})
def index():
    images = Image.query.order_by(Image.id.desc()).limit(10)
    map2={}
    for image in images:
        data1 = image_schema(image)
        data = [comment_schema(comment) for comment in image.comments]
        map2['comment_list'] = data
        data1.update(map2)
    return jsonify(data1)

这个是模式函数:

def image_schema(image):
    return {
        'id': image.id,
        'url': image.url,
        'created_date': image.created_date,
        'likecoun': image.likecount,
        'image_userid': image.user_id,
    }

def comment_schema(comment):
    return {'content': comment.content,
        'username': comment.user.username}

我一开始想用模式函数看看能不能实现,但是尝试了一下,失败告终。
非常谢谢greyli给我耐心指导:sob:


#10

合理。试试这样:

def image_and_comment_schema(image):
    # 这里只列出来三个字段,其他的你自己补充
    return {
        'id': image.id,
        'url': image.url,
        'comments': [{'content': comment.content, 'username': comment.user.username} for comment in image.comments]
    }

#11

后来我就用这个方法,就成功了,非常感谢!!