Presently, attempting to use ModelSchema
to model a SQLAlchemy model that contains a relationship that employs a one-to-one relationship, that is, they take advantage of the backref()
's uselist=False
argument, results in an TypeError
when attempting to serialize the relationship.
e.g. given something like… (note, this is pseudocode-y)
class Child(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
class Parent(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
child_id = db.Column(db.Integer, db.ForeignKey('child.id'))
only_child = db.relationship('Child', backref=db.backref('father', uselist=False))
class ChildSchema(ma.ModelSchema):
class Meta:
model = Child
class ParentSchema(ma.ModelSchema):
class Meta:
model = Parent
…performing a dump on a record is where the "fun starts"…
child = child.get_or_404(1)
child_schema = ChildSchema()
child_schema.dump(child).data
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-2a42b0948473> in <module>()
1 # hagbard = registrants.get_or_404(2)
----> 2 registrant_schema.dump(hagbard).data
/env/lib/python2.7/site-packages/marshmallow/schema.pyc in dump(self, obj, many, update_fields, **kwargs)
562 dict_class=self.dict_class,
563 index_errors=self.opts.index_errors,
--> 564 **kwargs
565 )
566 result = self._postprocess(preresult, many, obj=obj)
/env/lib/python2.7/site-packages/marshmallow/marshalling.pyc in serialize(self, obj, fields_dict, many, strict, skip_missing, accessor, dict_class, index_errors, index)
135 field_name=key,
136 field_obj=field_obj,
--> 137 index=(index if index_errors else None)
138 )
139 skip_conds = (
/env/lib/python2.7/site-packages/marshmallow/marshalling.pyc in call_and_store(self, getter_func, data, field_name, field_obj, index)
54 """
55 try:
---> 56 value = getter_func(data)
57 except ValidationError as err: # Store validation errors
58 self.error_fields.append(field_obj)
/env/lib/python2.7/site-packages/marshmallow/marshalling.pyc in <lambda>(d)
129 for attr_name, field_obj in iteritems(fields_dict):
130 key = ''.join([self.prefix, attr_name])
--> 131 getter = lambda d: field_obj.serialize(attr_name, d, accessor=accessor)
132 value = self.call_and_store(
133 getter_func=getter,
/env/lib/python2.7/site-packages/marshmallow/fields.py in serialize(self, attr, obj, accessor)
219 else:
220 return self.default
--> 221 return self._serialize(value, attr, obj)
222
223 def deserialize(self, value):
/env/lib/python2.7/site-packages/marshmallow/fields.py in _serialize(self, value, attr, obj)
1215 # else:
1216 # items = []
-> 1217 items = [self.keygetter(v) for v in value]
1218
1219 if not items:
TypeError: 'NoneType' object is not iterable
However, changing the Parent model's relationship to be only_child = db.relationship('Child', backref='father')
fixes the issue.
I suspect this is because with the uselist=False
, "relationship-oriented" attributes are no longer lists (even empty ones) but are rather simply None
values. I tried putting in some guards to prevent the issue if value were None (there on line 1217 where it's blowing up) but simply referencing the value
variable seemed to raise the error, e.g. even a if value is not None:
caused the same TypeError
.
I'd be really nice if this were supported, as this is the canonical way to represent 1:1 relationships AFAIK. Many kudos to you for an already great library, and thanks for your hard work!
(Also, while I realize this issue is coming up in marshmallow "proper", and might not be your purview, it seems to be a SQLAlchemy related issue and figured it would belong best here than under the vanilla marshmallow project's issues. If this is in error, I'm happy to open this issue there or elsewhere!)
q.v.: http://docs.sqlalchemy.org/en/rel_1_0/orm/basic_relationships.html#one-to-one