bulk_replace assumes incoming values are ORM objects

Issue #3896 resolved
Mike Bayer repo owner created an issue

There is no way to coerce incoming values to a collection without awkward use of two different APIs for full coverage:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.collections import collection

Base = declarative_base()


# awkward
class MyCollection(list):

    # awkward
    @collection.converter
    def convert(self, value):
        return [B(data=v['data']) for v in value]

    # awkward - can't even use collection.appender! 
    # so inconsistent!  "converter" does not fit with the other
    # methods at all, should be an event, and/or bulk_replace
    # needs a huge rethink
#    @collection.appender
#    def append(self, value):
#        # convert here - doesn't work

class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)

                           # awkward
    bs = relationship("B", collection_class=MyCollection)

    @validates('bs')
    def _go(self, key, value):
        # because of @converter, awkward
        if not isinstance(value, B):
            value = B(data=value['data'])
        return value


class B(Base):
    __tablename__ = 'b'
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey('a.id'))
    data = Column(String)


a1 = A()

# requires @converter
a1.bs = [{"data": "one"}, {"data": "two"}, {"data": "three"}]

# requres AttributeEvent / @validates
a1.bs.append({"data": "four"})

tentative 1.2 to consider a better system but this might be longer term

Comments (3)

  1. Mike Bayer reporter

    Add bulk_replace event, integrate with @validates

    Added new attribute event :meth:.AttributeEvents.bulk_replace. This event is triggered when a collection is assigned to a relationship, before the incoming collection is compared with the existing one. This early event allows for conversion of incoming non-ORM objects as well. The event is integrated with the @validates decorator.

    The @validates decorator now allows the decorated method to receive objects from a "bulk collection set" operation that have not yet been compared to the existing collection. This allows incoming values to be converted to compatible ORM objects as is already allowed from an "append" event. Note that this means that the @validates method is called for all values during a collection assignment, rather than just the ones that are new.

    Change-Id: I27f59db008d9e521d31a3e30143d7cd997e4b7b3 Fixes: #3896

    → <<cset 9974e9a46bdf>>

  2. Log in to comment