marshmallow-code / marshmallow-code/flask-marshmallow

backref uselist=False support

Open
#21 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
887
Forks
65
Avg merge
7h 25m
Merged PRs (30d)
3

Description

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)
in ()
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 (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

Contributor guide

Open the contributing guide

Research direction

Start with marshmallow/fields.py around line 1217 and reproduce the failure using the ParentSchema and ChildSchema example with a uselist=False relationship. Done means dumping that relationship no longer raises the reported TypeError when its value is None.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, sqlalchemy
Domain
database
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.