Dictionary support for one-to-many relations()

Issue #186 resolved
Former user created an issue

Something I'd love to see is having a one-to-many relation() (non-lazy at least) is to have a dictionary returned instead of a list or scalar, the key being a specified column from the joined table, breaking up the results on lists on the given keys.

Comments (3)

  1. Former user Account Deleted

    I was trying to implement it, but I got lost at: create_managed_attribute() in attributes. I still can't figure how UOWAttributeManager and it's subclasses works.

  2. Mike Bayer repo owner

    this capability exists right now, although it could benefit from more testing and documentation as well as some fixes to have it deal with generic dictionaries (right now you need a dictionary that is slightly modified). the specific feature you describe is acutally a subset of functionality. I think there should be an explcit option on relation() like you describe for this; I also think its reasonable that a particular column would be marked to be one of the "keys", however i would want it to be more flexible than that.

    so this raises the same old issue, that i want a very open-ended way to do it that makes anything possible, and theres a need for an "easy" way to do it, therefore there would be more than one way to do it.

    well heres the totally open-ended modified-dict-requiring way its done right now. Basically, you need a dict that has an append() method which handles picking the "key", and an __iter__() method that returns the values instead of the keys. This one is from one of the examples, examples/vertical/vertical.py:

    class EntityDict(dict):
        """this is a dictionary that implements an append() and an __iter__ method.
        such a dictionary can be used with SQLAlchemy list-based attributes."""
        def append(self, entityvalue):
            self[entityvalue.field.name](entityvalue.field.name) = entityvalue
        def __iter__(self):
            return iter(self.values())
    

    the two example scripts in examples/adjacencytree have examples of dict behavior as well.

    then make a class that will have a many relation on it like this:

    class Entity(object):
        # establish the type of '_entities' 
        _entities = EntityDict
    

    then when you make a mapper like this, it will use "EntityDict" as the list implementation:

    mapper(Entity, entities, properties = {
        '_entities' : relation(EntityValue, lazy=False, cascade='save-update')
    })
    

    the attributes package picks up on the EntityDict class in the register_attribute method and sends it along to create_prop.

    if you want to have more explicit support for this kind of thing, without requiring the iter and append() methods and instead having a callable or something that deals with get_key(value), the main place to look is in sqlalchemy.util.HistoryArraySet. This class needs a real overhaul, both for this concept as well as another thing id want to add eventually called "extra-lazy loading", which is a lazy loader that doesnt load the entire list of objects in one shot.

    also, if youre working with historyarrayset and attributes, keep in mind the rest of SA is not needed at all. dont deal with UOWAttributeManager as that is just an extension to the attributes module which adds event notification and is going to make the issue at hand more difficult to understand. take a look at test/attributes.py and test/masscreate2.py for examples of how to create "attribute managed" objects without using any SQL/ORM functionality.

  3. Log in to comment