如果association为None,则返回带有Flask-Restful Nested字段对象的空JSON对象,用于SQLAlchemy关联

时间:2014-11-11 14:37:38

标签: flask flask-sqlalchemy flask-restful

摘要可能会非常混乱,但我不知道如何更简洁地制定摘要。

我的模特:

class Movie(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(
    imdb_data = db.relationship('IMDBData', uselist=False)

class IMDBData(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255))
    rating = db.Column(db.Float)
    movie_id = db.Column(db.Integer, db.ForeignKey('movie.id'))

使用Flask-Restful字段我正在编组这样的响应:

imdb_data_fields = {
    'id': fields.Integer,
    'title': fields.String,
    'rating': fields.Float
}

movie_fields = {
    'id': fields.Integer,
    'title': fields.String
}

class MovieListAPI(Resource):
    def __init__(self):
        self.parser = reqparse.RequestParser()
        super(MovieListAPI, self).__init__()

    def get(self):
        self.parser.add_argument('imdb_data', type=str, location='args')
        args = self.parser.parse_args()

        m_fields = copy.copy(movie_fields)

        # TODO: Return empty object if movie.imdb_data = None
        if args['imdb_data']:
            m_fields['imdb_data'] = fields.Nested(imdb_data_fields)

        movies = Movie.query.all()
        return {'movies': marshal(movies, m_fields)}

现在,如果电影没有相应的imdb_data记录,即Movie.query.filter_by(id=123).first().imdb_data = None该电影的对象被编组为:

{
    "id": 1302, 
    "imdb_data": {
        "id": 0, 
        "rating": null, 
        "title": null
    },     
    "title": "F 63 9 Love Sickness"
}

相反,我希望响应看起来像这样:

{
    "id": 1302, 
    "imdb_data": {},     
    "title": "F 63 9 Love Sickness"
}

我知道如何在返回一部电影(通过id)时破解这个:

if args['imdb_data']:
    if movie.imdb_data:
        m_fields['imdb_data'] = fields.Nested(imdb_data_fields)
    else:
        m_fields['imdb_data'] = fields.Nested({})

但是我如何为电影列表做到这一点?可能我自己可以通过阵列并手动更改它,但必须有一个更有效的方法。

3 个答案:

答案 0 :(得分:3)

这可以通过创建自定义字段来实现,如下所示:

class NestedWithEmpty(Nested):
    """
    Allows returning an empty dictionary if marshaled value is None
    """
    def __init__(self, nested, allow_empty=False, **kwargs):
        self.allow_empty = allow_empty
        super(NestedWithEmpty, self).__init__(nested, **kwargs)

    def output(self, key, obj):
        value = get_value(key if self.attribute is None else self.attribute, obj)
        if value is None:
            if self.allow_null:
                return None
            elif self.allow_empty:
                return {}

        return marshal(value, self.nested)

然后用它来封送通过allow_empty=True的对象:

m_fields['imdb_data'] = NestedWithEmpty(imdb_data_fields, allow_empty=True)

我甚至使用此功能创建了一个拉取请求:https://github.com/twilio/flask-restful/pull/328

答案 1 :(得分:1)

在阅读PR#328(感谢@Andriy)并关注它之后,我的修复是添加默认 arg

foo['bar'] = fields.Nested(nested_fields, default={})

在文档中并不明显。

答案 2 :(得分:0)

从0.11.0开始,可以使用选项skip_none=True返回空对象而不是null。

@marshal_with的示例:

from flask_restplus import Model, fields, marshal_with
model = Model('Model', {
    'name': fields.String,
    'address_1': fields.String,
    'address_2': fields.String
})
@marshal_with(model, skip_none=True)
    def get():
        return {'name': 'John', 'address_1': None}

指定嵌套字段:

from flask_restplus import Model, fields
model = Model('Model', {
    'name': fields.String,
    'location': fields.Nested(location_model, skip_none=True)
})

来源:https://flask-restplus.readthedocs.io/en/0.11.0/marshalling.html#skip-fields-which-value-is-none