Nested composite column types

Issue #4168 closed
Psyche NA
created an issue

According to the document

I have a Point class:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __composite_values__(self):
        return self.x, self.y

I could create a mapping to a table vertices, which represents two points as x1/y1 and x2/y2.

class Vertex(Base):
    __tablename__ = 'vertices'

    id = Column(Integer, primary_key=True)
    x1 = Column(Integer)
    y1 = Column(Integer)
    x2 = Column(Integer)
    y2 = Column(Integer)

    start = composite(Point, x1, y1)
    end = composite(Point, x2, y2)

But the problem is that I have two classes Point and Line:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y


class Line(object):
    def __init__(self, start: Point, end: Point):
        self.start = start
        self.end = end

I could not create a mapping to a table Shape like this:

class Shape(Base):
    __tablename__ = 'shapes'

    id = Column(Integer, primary_key=True)
    a1 = Column(Integer)
    a2 = Column(Integer)
    b1 = Column(Integer)
    b2 = Column(Integer)
    c1 = Column(Integer)
    c2 = Column(Integer)
    d1 = Column(Integer)
    d2 = Column(Integer)
    ...

    line1 = composite(Line, composite(Point, a1, a2), composite(Point, b1, b2))
    line2 = composite(Line, composite(Point, c1, c2), composite(Point, d1, d2))
    ...

Maybe the example above is not good enough. But the point is that could composite be nested?

Comments (4)

  1. Michael Bayer repo owner

    they don't support nesting and to implement this would be very complicated. you're much better off just using hybrid attributes which can get you 95% of what composites do and can be organized in any way you'd like.

  2. Psyche NA reporter

    Thanks for your reply ^-^ Finally I found a solution for this feature.

    In descriptor_props.py

    # line 293
    # assert self.key not in dict_
    dict_[self.key] = self.composite_class(
        *[state.dict[key] for key in
           self._attribute_keys]
    )
    

    self.composite_class is the class type predefined in composite function. So I created a classmethod in Line, and pass it to composite like this:

    class Line(object):
        def __init__(self, start: Point, end: Point):
            self.start = start
            self.end = end
    
        @classmethod
        def create(cls, a1, a2, b1, b2):
            return cls(Point(a1, a2), Point(b1, b2)
    
    class Shape(Base):
        __tablename__ = 'shapes'
    
        id = Column(Integer, primary_key=True)
        a1 = Column(Integer)
        a2 = Column(Integer)
        b1 = Column(Integer)
        b2 = Column(Integer)
        ...
    
        line1 = composite(Line.create, a1, a2, b1, b2)
        line2 = composite(Line.create, c1, c2, d1, d2)
    

    This solution worked.

  3. Michael Bayer repo owner

    sure, if that gets you what you need, technically Point is no longer a "composite" from an ORM point of view, meaning you cant query from it or anything like that....although, you could put a hybrid onto Line so that you could still do Shape.line1.start == Point(5, 6).

  4. Michael Bayer repo owner

    you can get nesting of composites pretty much using classmethods and/or @hybrid_property on your top composite, this is much simpler than trying to build nesting all the way into the composite descriptor which is already difficult to extend.

  5. Log in to comment