代码之家  ›  专栏  ›  技术社区  ›  ken

如何将2个表中的数据组合成单个数据响应?

  •  -2
  • ken  · 技术社区  · 7 年前

    details = db.session.query(User, Device).filter(User.id == Device.id) \
                        .filter(User.email== data['email'])\
                        .all()
    

    目前,可以在控制台中打印上述查询的结果如下:

    [(<User 'None'>, <Device 1>)]
    

    我希望API端点返回以下JSON:

    {
      "data": [
        [
          {
            "id": 20,
            "name": null,
            "token": "Some String here"
          }
        ]
      ]
    }
    

    这是我的DTO:

    class UserDto:
        # this is for input
        user = api.model('user', {
            'email': fields.String(required=False, description='phone number'),
            'name': fields.String(required=False, description='username'),
            'device_id': fields.String(required=False,description='user_device_id'),
       })
    
       # this is for output
        details = api.model('details', {
            'id': fields.Integer(required=False, description='the id'),
            'name': fields.String(required=False, description='name'),
            'token': fields.String(required=False, description='token')
        })
    

    User Device

    class User(db.Model):
        __tablename__ = "users_info"
    
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        name = db.Column(db.Integer, unique=True, nullable=True)
        email = db.Column(db.String)
        device = db.relationship('Device', backref='user')
        # .. more fields ..
    
    
    class Device(db.Model):
        __tablename__ = "user_device"
    
        user_device_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        id = db.Column(db.Integer, db.ForeignKey(User.id))
        token = db.Column(db.String, nullable=True)
        # .. more fields ..
    

    为了实现上面的JSON结果,我希望 id name 对象,以及 token 是从 装置

    控制器:

    api = UserDto.api
    _user = UserDto.user
    _details = UserDto.details
    
    @api.route('/')
    class User(Resource):
        @api.response(201, 'successful')
        @api.expect(_user, validate=True)
        @api.marshal_list_with(_details, envelope='data')
        def post(self):
            data = request.json
            return user(data=data)
    

    实际答复:

    {
      "data": [
        [
          {
            "id": 20,
            "name": null,
            "token": null
          },
          {
            "id": 20,
            "name": null,
            "token": "some string here"
          }
        ]
      ]
    }
    

    代币 null 还有一次 代币 用我想要的绳子)。

    我如何才能获得我想要的上述响应?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Martijn Pieters    7 年前

    由于查询中有两个对象(行),因此会得到两个输出项。您不需要添加 Device 对象,因为它已作为 user.device . 调整您的Restplus模型以使用 user.device.token

    因此,您需要更改查询以仅加载用户:

    return User.query.filter_by(email == data['email']).all()
    

    User.email 字段应该是唯一的(电子邮件地址应该只引用数据库中的一个用户),请考虑使用 .first() .all() .first() 在列表中,或者更符合逻辑的方式,将返回值更改为没有列表的单个JSON对象。也考虑使用 first_or_404() method 自动生成 404 Not Found 如果没有用户使用此电子邮件,则响应。

    然后,您需要配置 details API模型:

    details = api.model('details', {
        'id': fields.Integer(required=False, description='the id'),
        'name': fields.String(required=False, description='name'),
        'token': fields.String(
            attribute='device.token',
            required=False,
            description='token'
        )
    })
    

    attribute 字段 token 代币 属性 device 每个对象的属性。

    one to many 所以 实际上是一个列表。添加 uselist=False 给你 装置

    class User(db.Model):
        __tablename__ = "users_info"
    
        # ...
        device = db.relationship('Device', backref='user', uselist=False)
    

    这就把你限制在 价值,所以是单一的 装置 对象/用户。

    或者,如果你真的想让它成为一对多的关系,那么你必须替换 代币 tokens 列表 :

    details = api.model('details', {
        'id': fields.Integer(required=False, description='the id'),
        'name': fields.String(required=False, description='name'),
        'token': fields.List(
            fields.String(attribute='token', required=False),
            description='array of tokens for all user devices',
            attribute='device'
        )
    })
    

    您可能需要重命名 装置 devices 在这种情况下,为了更好地反映您有多个。

    请注意,在所有情况下,您都需要考虑 loading strategies . 如果您总是要访问与用户关联的设备,请添加 lazy="joined" 装置 这种关系有助于提高绩效。或者,添加 .options(db.joinedload('device')) User 查询,在 .first() 电话:

    return User.query.filter_by(email == data['email']).options(db.joinedload('device')).all()