Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get dict of lists from relationship in sqlalchemy?

I've found out that you can use collections in relationship in order to change the type of return value, specifically I was interested in dictionaries. Documentation gives an example:

class Item(Base):
    __tablename__ = 'item'
    id = Column(Integer, primary_key=True)
    notes = relationship("Note",
                         collection_class=attribute_mapped_collection('keyword'),
                         cascade="all, delete-orphan")

class Note(Base):
    __tablename__ = 'note'
    id = Column(Integer, primary_key=True)
    item_id = Column(Integer, ForeignKey('item.id'), nullable=False)
    keyword = Column(String)
    text = Column(String)

And it works. However I was hoping that it will make list values if there are more than just one key with the same name. But it only puts the last value under unique key name.

Here is an example:

|               Note table               |
|---------------------|------------------|
|          id         |      keyword     |
|---------------------|------------------|
|          1          |        foo       |
|---------------------|------------------|
|          2          |        foo       |
|---------------------|------------------|
|          3          |        bar       |
|---------------------|------------------|
|          4          |        bar       |
|---------------------|------------------|

item.notes will return something like this:

{'foo': <project.models.note.Note at 0x7fc6840fadd2>,
 'bar': <project.models.note.Note at 0x7fc6840fadd4>}

Where ids of foo and bar objects are 2 and 4 respectively.

What I'm looking for is to get something like this:

{'foo': [<project.models.note.Note at 0x7fc6840fadd1,
          <project.models.note.Note at 0x7fc6840fadd2>],
 'bar': [<project.models.note.Note at 0x7fc6840fadd3>,
         <project.models.note.Note at 0x7fc6840fadd4>]}

Is it possible to get dict of lists from relationship in sqlalchemy?

like image 980
Keipa Glows Avatar asked Aug 04 '16 08:08

Keipa Glows


1 Answers

So, it turns out you can simply inherit MappedCollection and do whatever you like in setitem there.

from sqlalchemy.orm.collections import (MappedCollection,
                                        _SerializableAttrGetter,
                                        collection,
                                        _instrument_class)

#This will ensure that the MappedCollection has been properly 
#initialized with custom __setitem__() and __delitem__() methods 
#before used in a custom subclass
_instrument_class(MappedCollection)


class DictOfListsCollection(MappedCollection):

    @collection.internally_instrumented
    def __setitem__(self, key, value, _sa_initiator=None):
        if not super(DictOfListsCollection, self).get(key):
            super(DictOfListsCollection, self).__setitem__(key, [], _sa_initiator)
        super(DictOfListsCollection, self).__getitem__(key).append(value)
like image 152
Keipa Glows Avatar answered Nov 15 '22 09:11

Keipa Glows