marshmallow-code/marshmallow-sqlalchemy

Data in from a many-to-many association object

antgel opened this issue · 4 comments

I'm using the example of a party co-ordination system, where many Parties can have many Responders. I have an association object PartyResponder which maps the many-to-many, and adds another few columns such as is_attending (maybe the responder will come to the party, maybe they won't).

class PartySchema(ma.ModelSchema):
    class Meta:
        model = Party

    responders = ma.Nested(
        ResponderSchema, many=True, exclude=["responder_parties", "parties"]
    )

class ResponderSchema(ma.ModelSchema):
    class Meta:
        model = Responder

    parties = ma.Nested(
        "PartySchema", many=True, exclude=["party_responders", "responders"]
    )

class PartyResponderSchema(ma.ModelSchema):
    class Meta:
        model = PartyResponder

The JSON I currently get from my API (via PartySchema when I do something like http GET http://localhost:5000/parties/1/) looks something like this. (I've removed irrelevant parts.)

{
  "id": 1, 
  "party_responders": [
    {
      "party_id": 1, 
      "responder_id": 1
    }, 
    {
      "party_id": 1, 
      "responder_id": 2
    }
  ], 
  "responders": [
    {
      "id": 1, 
      "name": "Bob Smith", 
      "username": "bob"
    }, 
    {
 
      "id": 2, 
      "name": "John Doe", 
      "username": "john"
    }
  ], 
}

The Nested relationships behave as intended, I can see who has responded to the party in the responders array of objects, but what is the sanest way to get access to the association object to get to the is_attending field? As you see from the first code paste, neither PartySchema nor ResponderSchema know about PartyResponderSchema. Will that need to change in order to do this? It feels like something that could quickly hit maximum recursion depth.

Could you add the is_attending as a property on Responder and add that to ResponderSchema?

I'm not sure I'm understanding the desired behavior.

@sloria I'll try to clarify. It's many-to-many, a Responder can attend many Parties, so it doesn't make sense that is_attending is an attribute on Responder. Example sample data:

responder table:
id name
1  antgel
2  sloria

party table:
id name
1  Notting Hill Carnival
2  Full Moon Party

party_responder table / association object:
party_id responder_id is_attending
1        1            true
1        2            false
2        1            true

(sloria hasn't yet responded to the Full Moon Party, party_responder tracks both attending and not attending responses... )

Hope this gives a better insight.

You can probably do some hackery by setting the party on the schema context and using a Method field.

But stepping back, do you actually need to do this? Having properties like is_attending that vary on a resource based on a related resource is an anti-pattern. Instead, you should model the association as its own resource, accessible at its own URL. In your case, it'd be something like parties/1/relationships/responders. That would return the data from your PartyResponderSchema. You could nest or sideload the repsonders and parties if you needed to.

Closing for now