Schema deserialisation with defined session and nested field throws an exception #67
Comments
Yes, the intended behavior is that the session propagates to nested fields. What version of marshmallow-sqlalchemy and marshmallow are you using? @nickw444 |
Using:
I'll try reproduce this in a standalone flask app |
Here's a minimal example: from flask import Flask, request
from marshmallow import fields, validate
from marshmallow_sqlalchemy import ModelSchema
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
class NestedModel(db.Model):
__tablename__ = "nested_model"
id = db.Column(db.Integer(), primary_key=True)
field1 = db.Column(db.String(255))
field2 = db.Column(db.String(255))
class TopLevelModel(db.Model):
__tablename__ = "top_level_model"
id = db.Column(db.Integer(), primary_key=True)
nested_id = db.Column(db.Integer(), db.ForeignKey('nested_model.id'))
nested = db.relationship('NestedModel')
field1 = db.Column(db.String(255))
class MyNested(ModelSchema):
field1 = fields.String()
field2 = fields.String()
class TopLevel(ModelSchema):
nested = fields.Nested(MyNested())
field1 = fields.String()
@app.route('/', methods=['POST'])
def index():
obj = TopLevelModel()
res = TopLevel().load(
request.json,
instance=obj,
session=db.session
)
print(res)
print(obj)
print(obj.nested_id)
print(obj.nested)
return ""
if __name__ == '__main__':
app.run(debug=True) Posting this payload:
Results in this traceback
|
I'm having the same exact problem (same versions), is there any fix for that? |
Also having the same problem, got the latest version, is it still not fixed? |
I'm also having the same problem, would appreciate knowing if this is being fixed. |
I ended up writing this field type to solve the problem: https://github.com/TwoPiCode/marshmallow-sqlalchemy-referential. It makes the interface a bit more restful too - for example, you aren't creating a nested object, but rather creating an association. The README is a bit bare, but the docstring describes the usage:
Obviously the objects you are associating would already need to have been created (maybe through another API endpoint)? |
It looks like this bug is unresolved? @sloria indicated back on May 30 that a passed session SHOULD propagate to nested fields, but it looks like it DOESN'T, even now, and this has not been addressed? |
I've been using a workaround by manually de-serialising nested objects, but yes, I agree, this is not an optimal solution. |
It looks like even if you specify "sqla_session" for the TopLevel schema definition, it doesn't get passed along to the Nested schemas, so Nested() is broken completely except where the nested schema itself specifies a "sqla_session" in its definition? #permSession and tempSession are a couple of sqlAlchemy sessions
class MySchema(ModelSchema):
...
schema = MySchema(session=permSession) # always use permSession unless overridden by load()
result = schema.load(data) # will use permSession
result = schema.load(data, session=tempSession) # will use tempSession
result = schema.load(data) # should use permSession, but previous load has set schema.session to tempSession but never restored it to permSession. I haven't tested this, but scanning the code seems to suggest that's what would happen. |
What you guys think about the solution presented by @torypages on marshmallow-code/marshmallow#658? Couldn't we just convert the |
First version of flask-rest-orm was using marshmallow-sqlalchemy library to serialize SQLAlchemy models. Using marshmallow-sqlalchemy lead to two main problems: 1. We were doing a lot of workarrounds due the fact that marshmallow-sqlalchemy does not support some basic features of SQLAlchemy like 'column_properties' and nested objects. 2. marshmallow-sqlalchemy serializers require a DB Session object to work, since they take the responsibility to commit and load objects from the DB. But marshmallow-sqlalchemy has some issues to access the Session when dealing with nested objects (see marshmallow-code/marshmallow-sqlalchemy#67). There is also the fact that for a REST library, it is more consistent to do commits and loads on the Resource object (on GET and POST methods), and use the serializer just to instancialize and jsonify the models. So this commit replaces the marshmallow-sqlalchemy serializer by our own implementation in class ModelSerializer. ModelSerializer automatically inspect SQLAlchemy model columns and serialize to the correct type. Additional properties could be serialized by extending ModelSerializer and decliaring Field objects for it. Nested models can be serialized with NestedModelFields. All features of flask-rest-orm were preserved.
First version of flask-rest-orm was using marshmallow-sqlalchemy library to serialize SQLAlchemy models. Using marshmallow-sqlalchemy lead to two main problems: 1. We were doing a lot of workarrounds due the fact that marshmallow-sqlalchemy does not support some basic features of SQLAlchemy like 'column_properties' and nested objects. 2. marshmallow-sqlalchemy serializers require a DB Session object to work, since they take the responsibility to commit and load objects from the DB. But marshmallow-sqlalchemy has some issues to access the Session when dealing with nested objects (see marshmallow-code/marshmallow-sqlalchemy#67). There is also the fact that for a REST library, it is more consistent to do commits and loads on the Resource object (on GET and POST methods), and use the serializer just to instancialize and jsonify the models. So this commit replaces the marshmallow-sqlalchemy serializer by our own implementation in class ModelSerializer. ModelSerializer automatically inspect SQLAlchemy model columns and serialize to the correct type. Additional properties could be serialized by extending ModelSerializer and decliaring Field objects for it. Nested models can be serialized with NestedModelFields. All features of flask-rest-orm were preserved.
I managed to implement a fix that builds upon the one linked in the comment above, making it general-purpose (you don't need to know the names of your nested fields to use this):
|
Okay so to summarize discussions so far, this is still an outstanding issue (in 2019). There are a few workarounds (notably @MattF-NSIDC's one above), but ideally this shouldn't be needed. This issue has been discussed in other projects upstream and downstream of marshmallow-sqlalchemy, but the conclusion is that it needs to be fixed here. Would it not be reasonable for us to create our own |
Thanks all for the discussion and the workarounds. I know this is still an issue, and I'm sorry I haven't been able to address this. I won't have much time to work on this in the near future (busy with work, and also focusing on getting marshmallow 3.0 final out). Would gladly review/merge a PR addressing this issue. Haven't given much thought as to the proper solution, but I think @TMiguelT 's suggestion of a custom |
* Allow session to be passed to nested fields. Fixes #67 * Update tests/test_marshmallow_sqlalchemy.py Respond to feedback. Co-Authored-By: Steven Loria <sloria1@gmail.com> * Allow many (and all args) to be passed through * Fix formatting; update changelog * Update recipes to use ma-sqla's Nested
Not sure if this is intended functionality, however the code below throws
ValueError: Deserialization requires a session
when trying to load data into a nested object.I would expect passing a session would propagate to nested fields, however this doesn't seem to be the case. Is this by design?
If i remove
followup
(the nested field) from theonly=
property, the data loads fine from the non-nested field.The text was updated successfully, but these errors were encountered: