Hello everyone...

I'm trying to use a custom collection to "connect" (or relate) two
classes but I haven't been able to do it. Maybe I got the whole
concept of the custom collections wrong, but let me explain what I am
doing (and see if someone can give me a hint, or something)

I have a Parent class (which some of you will remember from other
questions) with a couple of children. One of the children fields
stores children whose type is "VR" and the other children with a "CC"
type.

I don't really need persistence for the collection used to store the
children, but I need it to be of an special class so it will have some
methods that I have implemented and that need to be there. That would
be the "ZepConnector" (and, for purposes of the example, it's method
foo() it's the one I need to use). As you can see in the following
lines, I randomly test its availability in the addChild1() method of
the Parent.

--------------------- Parent.py -----------------

from megrok import rdb
from sqlalchemy import Column
from sqlalchemy import and_
from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer
from sqlalchemy.types import String
from mylibraries.database.tests.Child import Child
from mylibraries.database.tests.Tables import testMetadata
from mylibraries.database.tests.ZepConnector import ZepConnector

class Parent(rdb.Model):
        rdb.metadata(testMetadata)
        rdb.tablename("parents_table")
        rdb.tableargs(schema='test2', useexisting=False)

        id = Column("id", Integer, primary_key=True, nullable=False, 
unique=True)
        _whateverField1 = Column("whatever_field1", String(16)) #Irrelevant
        _whateverField2 = Column("whatever_field2", String(16)) #Irrelevant

        child1 = relationship(
                "Child",
                uselist=True,
                primaryjoin=lambda: and_((Parent.id == Child.parent_id), 
(Child.type
== "VR")),
                collection_class=ZepConnector("VR")
                )
                
        child2 = relationship(
                "Child",
                uselist=True,
                primaryjoin=lambda: and_((Parent.id == Child.parent_id), 
(Child.type
== "CC")),
                collection_class=ZepConnector("CC")
                )
                
        def __init__(self):
                print "Parent __init__"
                self._whateverField1 = "Whatever1"
                self._whateverField2 = "Whatever2"
                self.child1 = ZepConnector("VR")
                self.child2 = ZepConnector("CC")

        def addChild1(self, child):
                if isinstance(child, Child):
                        print("::addChild1 > Testing .foo method: " + 
str(self.child1.foo()))   
                        # The line above doesn't really makes much but testing 
the
accessibility of the .foo() method.
                        # As I explain later, it doesn't work
                        self.child1.append(child)

        def addChild2(self, child):
                if isinstance(child, Child):
                        self.child2.append(child)

----------------------------------------------------

Please note that I'm using megrok. For those who are not familiar with
it, allow me to explain that it is just a tool that writes the mappers
itself and makes it a little bit "programmer friendly".

I guess The mapping of the Parent() class in regular SqlAlchemy would
be something like:

mapper(Parent, parents_table, properties={
        id = Column("id", Integer, primary_key=True, nullable=False, 
unique=True)
        _whateverField1 = Column("whatever_field1", String(16)) #Irrelevant
        _whateverField2 = Column("whatever_field2", String(16)) #Irrelevant
        child1 = relationship( # etc, etc, etc
})
#

but I'm 100%... erm... 90% certain that using that tool is not what
lead me to ask what I'm going to ask here (I mean: I don't think is
interfering with the Collections thing)


A child is a very simple class:

--------------- Child.py --------------------------

import random

from megrok import rdb
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy.types import Integer
from sqlalchemy.types import String
from mylibraries.database.tests.Tables import testMetadata

class Child(rdb.Model):
        rdb.metadata(testMetadata)
        rdb.tablename("children_table")
        rdb.tableargs(schema='test2', useexisting=False)
        
        parent_id = Column("parent_id", Integer,
ForeignKey("test2.parents_table.id"), primary_key=True)
        type = Column("type", String(2), nullable=True, primary_key=True)
        hasher = Column("hasher", String(5))
        
        def __init__(self):
                self.type = None
                self.hasher = self.generateHasher()

        def setType(self, typeParameter):
                if typeParameter in set(["VR", "CC"]):
                        self.type = typeParameter

        @staticmethod
        def generateHasher():
                retval = str()
                for i in 
random.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
5):
                        retval += i
                return retval
---------------------------------------------------------

Let's say every Child instance will have a unique "hasher" field that
can be used as a key in a dictionary (the example above is far away
from the reality, but it illustrates a little bit how the Child will
work)

And now my custom connector. I want it to behave as a list or a set
(more like a set, although I don't mind much) but it's a class that
inherits from dict.

-------------------- ZepConnector.py --------------------

from sqlalchemy.orm.collections import collection

class ZepConnector(dict):
        __emulates__ = list
        
        def __init__(self, type):
                self.type = type

        def foo(self):
                return True

        @collection.appender
        def append(self, item):
                #Appends a child to itself
                if self.foo():
                        item.setType(self.type)
                        self[item.hasher] = item

        @collection.remover
        def remove(self, item):
                try:
                        del self[item.hasher]
                except ValueError, e:
                        print("::remove > Got exception when trying to remove 
entry=" +
str(item.hasher) + ". The exception is: " + str(e))

        def extend(self, items):
                pass
---------------------------------------------------------

But I don't know why, the "ZepConnector" instances in the Parent class
don't seem to be of a "ZepConnector" type but of an
"InstrumentedList":

When in the addChild1 method of Parent() I try to test the .foo()
method (which should just return True) I get this error:
        AttributeError: 'InstrumentedList' object has no attribute 'foo'

It's strange... The __init__ method of the ZepConnector is properly
executed... but when I try to use it, it is not a ZepConnector...

In a second try I wrote:

        class ZepConnector(dict):
                __emulates__ = set

but this even makes things worse, because I get:
                TypeError: Incompatible collection type: ZepConnector is not 
list-like

In a third (or second point second) try, I though... "well... if it's
saying that ZepConnector is not a list, maybe telling the Parent() not
to use a list in the relationship may help... Maybe stating that the
collection_class is a ZepConnector makes unnecessary the uselist
parameter in the relationship..."

And so I wrote:

                child1 = relationship(
                        "Child",
                        uselist = False,
                        primaryjoin=lambda: and_((Parent.id == Child.parent_id),
(Child.type == "VR")),
                        collection_class=ZepConnector("VR")
                        )

But that threw a creepy exception talking about a field which I
shouldn't see and that I don't want to see... ever...
:-D
                AttributeError: 'ZepConnector' object has no attribute 
'_sa_instance_state'

If someone has any ideas, guidance, counseling... whatever... I'd
really appreciate you sharing it with me... erm... us...

Thank you in advance!

(if you have reached this line, you certainly deserve a "thank you"
just for your patience reading this huge email)

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

Reply via email to