On Mar 3, 2012, at 10:03 AM, Peter Erickson wrote:

> Is is possible to set the polymorphic_on attribute on an object that is
> not directly tied to a db table, but has access to the db attribute via
> delegation? 
> 
> I have a generic Product class that processes an XML to obtain its
> generic attributes (uuid, type, etc). Afterwards, the product is
> encapsulated within one of several proxy objects that perform additional
> processing based on the enclosed product type. Each of the various
> proxies are derived from a BaseProxy and multiple types can use
> the same proxy. Due to this, the BaseProxy has a poly_type to specify
> which of the proxies was used for additional processing.
> 
> Base = declarative_base()
> 
> class Product(Base):
>    __tablename__ = 'products'
> 
> class BaseProxy(Base):
>    __tablename__ = 'products'
> 
>    product = relationship(Product, uselist=False)
> 
> 
> class HardwareProduct(BaseProxy, Base):
>    __tablename__ = 'hardware'
> 
> class SoftwareProduct(BaseProxy, Base):
>    __tablename__ = 'software'
>    __mapper_args__ = {'polymorphic_identity': 'software'}
> 
> In anticipation of one response, the reason that I didnt have Hardware
> and SoftwareProduct inherit directly from Product was because the
> product's type wasn't known until after product creation and, due to the
> number of products created, I didn't want to waste the time recreating
> the underlying product again and again. However, I'm willing to change
> it if there is a better approach. Product creation looks something like:
> 
> p = Product('product.xml')
> p = select_proxy_based_on_type(p.type)(p)


There's a few ways I can answer this question.    There's addressing the 
mapping itself, and ways to have a "product" attribute on both HardwareProduct 
and SoftwareProduct.   But I think first let's see about the rationale, which 
is that you want to create a "Product", which doesn't know its real type yet, 
then using composition (that is, Product points to HardwareProduct or 
SoftwareProduct), you'd add extra attributes to Product.

So right off I think trying to define the type of "Product" using composition 
is leading to complexity and I'd want to try to do it more simply.   The 
"normal" way to do this is just to make Product, then have some method 
"Product.coerce_to_type()" that copies itself over to a new SoftwareProduct or 
HardwareProduct.    If I were writing this app, I would probably do it that 
way, as it is the simplest and most straightforward approach.     But note 
there is a key assumption here, which is that you don't need to actually 
*persist* Product until its type is known.  If you do actually need to persist 
Product with a "generic" type, then change the type in the database, we need to 
take that into account.

Let's first assume the main rationale is performance concerns about generating 
a new Product plus a new XYZProduct, that is generating two objects instead of 
one.   I'd first make sure that the performance overhead of creating Product, 
then XYZProduct, is definitely prohibitive.     So one way to cut down on that, 
while maintaining a very simple approach, is to just collect the data for the 
XYZProduct into a lighter weight structure, such as a dictionary, or a 
ProductInfo(object) type of object that isn't actually mapped and therefore has 
no instrumentation overhead.      

The other way to do this, which is a bit more hacky, is to change the class of 
Product once constructed:

p = Product('product.xml')
p.__class__ = SoftwareProduct
p.type = "software"

It's this second approach that can conceivably be used with a Product that's 
already persisted in the database, too.    SQLA doesn't support the "changing 
the class" operation directly though, so you'd need to do an INSERT statement 
manually on the related table, then re-add a new XYZProduct object in.

Anyway, I'd go with Product("xyz").cast_as("software"), returning me a new 
SoftwareProduct object, just because that would work the most simply.    

If you want to explore the "Product.product" approach, we can talk about that 
also though I think it would work with BaseProxy being mapped as a "concrete" 
class to "hardware" and "software", at the moment it seems like pulling in 
"products" into BaseProxy makes this more complicated.












> 
> I realize this is a drawn out question, but any help is appreciated.
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to