Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy polymorphic association

I try to implement polymorphic association by mean of SQLAlchemy library and use for it the example:

discriminator_on_association.

But there is one incompatibility with my case. In the example: Address objects created and then they added to Supplier or Customer (so Address table has polymorphic foreign key to the Supplier and Customer tables and Supplier and Customer object backref to the Address). While Address object add to the one these tables SQLAlchemy automatically solve how to assign Id's on the tables. Then Supplier or Customer object can be obtained using 'parent' field of the Address.

Though in my case I need first create Address object and then assign to the address parent an object (e.g. Company, in example it was Supplier and Customer).

Example of the using polymorphic association as for my case:

engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)

session = Session(engine)

address = Address(street='2569 west elm', city="Detroit", zip="56785")

cust = Customer(name="Tester")
supl = Supplier(company_name="Ace Hammers")

address.parent = supl

session.add_all([address])

session.commit()

But if I try to apply this pattern to my case without modification I receive the next error message:

Traceback (most recent call last):
  File "discriminator_on_related.py", line 131, in 
    address.parent = supl
  File "C:\Python27\lib\site-packages\sqlalchemy-0.9.3-py2.7-win32.egg\sqlalchemy\ext\associationproxy.py", line 271, in __set__
    setattr(obj, self.target_collection, creator(values))
TypeError: __init__() takes exactly 1 argument (2 given)

How I should modify this pattern to make it works?

P.S. Sorry if it's trivial question (SQLAlchemy association_proxy looks like some kind of magic for me).

like image 493
mblw Avatar asked Mar 24 '14 06:03

mblw


1 Answers

Sorry everyone who was bothered in this question. May be the answer will be useful for someone. The answer was really too easy. The main my mistake was that I've use a complicated pattern example as base on my case. To solve this task was only required to split it on subtasks. So this task consist of polymorphic inheritance with joined tables (that implement quite easy using SQLAlchemy) and setting foreign key on common (parent) table. That's show the next code snippet:

@as_declarative()
class Base(object):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
    id = Column(Integer, primary_key=True)


class CompanyInterface(Base):
    discriminator = Column(String)

    __mapper_args__ = {"polymorphic_on": discriminator}


class Address(Base):
    street = Column(String)
    city = Column(String)
    zip = Column(String)
    company_id = Column(Integer, ForeignKey(CompanyInterface.id))
    company = relationship(CompanyInterface)

    def __repr__(self):
        return ("%s(street=%r, city=%r, zip=%r, company=%r)" %
                (self.__class__.__name__, self.street, self.city, self.zip, self.company))


class Customer(CompanyInterface):
    id = Column(Integer, ForeignKey(CompanyInterface.id), primary_key=True)
    name = Column(String)
    __mapper_args__ = {"polymorphic_identity": "Customer"}


class Supplier(CompanyInterface):
    id = Column(Integer, ForeignKey(CompanyInterface.id), primary_key=True)
    company_name = Column(String)
    __mapper_args__ = {"polymorphic_identity": "Supplier"}


engine = create_engine('sqlite:///test/test_me.db', echo=False)
Base.metadata.create_all(engine)

session = Session(engine)

address1 = Address(street='test-1', city="Detroit", zip="56785")
address2 = Address(street='test-2', city="Phoenix", zip="110322")
address3 = Address(street='test-3', city="Washington", zip="432414")

supl1 = Supplier(company_name="Supplier-1 TEST")
supl2 = Supplier(company_name="Supplier-2 TEST")
cust1 = Customer(name="Customer-1 TEST")
cust2 = Customer(name="Customer-2 TEST")

address1.company = supl1
address2.company = cust1
address3.company = cust1

session.add_all([address1, address2, address3])

session.commit()

address3.company = supl2

session.commit()

print "PRINTING, TOTAL = %s" % session.query(Address).count()
for address in session.query(Address):
    print "ADDRESS = %s" % address
like image 139
mblw Avatar answered Oct 01 '22 03:10

mblw