2010-11-09 26 views
0

我试图使用自定义集合“连接”(或相关)两个类,但我一直无法做到这一点。也许我弄错了SqlAlchemy custom collections的整个概念,但让我解释我在做什么(并看看有人能给我一个提示,或某事)使用从字典()与SqlAlchemy扩展自定义集合

我有一个父类(你会记得一些其他问题),其中有几个连接器字段(列表类型)。其中一个连接器将存储类型为“VR”的Child()类的实例,另一个将存储具有“CC”类型的子级。

我真的不需要持久化存储子对象的集合,但我需要它是一个特殊的类,所以它会有一些我已经实现的方法,并且需要在那里。这将是“ZepConnector”类(为了举例的目的,它的方法foo()是我需要使用的)。正如您在以下几行中看到的那样,我在Parent的addChild1()方法中随机测试其可用性。

--------------------- 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'll explain later, it doesn't work 
      self.child1.append(child) 

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

请注意,我正在使用megrok。对于那些不熟悉它的人,请允许我解释一下,它仅仅是一个将Python类映射到SqlAlchemy映射器本身的工具,并且在使用Grok framework时使它更加“程序员友好型”。

我猜父()类的常规SQLAlchemy的映射将类似这样的:

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 
}) 
# 

但我100%...呃... 90%...呃.. 70%的人认为使用该工具并不是什么导致我问我在这里要问什么(我的意思是:我不认为是在干涉SqlAlchemy Custom Collections的事情)

一个孩子是一个非常简单课:

--------------- 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('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 5): 
      retval += i 
     return retval 

比方说,每一个孩子实例都会有一个独特的“散列器”字段,可以用来作为在字典中键(上面的例子是远离现实,但它说明了一点如何孩子将工作,并能够创建一个测试)

现在我的自定义连接器。我希望它的行为如同列表集合(更像是集合,虽然我不介意),但它是一个继承自字典的类。

-------------------- ZepConnector。PY --------------------

from sqlalchemy.orm.collections import collection 

class ZepConnector(dict): 
    __emulates__ = list 

    def __init__(self, type): 
     self.type = type 
     # The 'type' will be "VR" or "CC" and it will be stamped 
     # on every Child() class added through this ZepConnector 

    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 

但我不知道为什么,在父类中唐 “ZepConnector” 实例”不像是会是一个“ZepConnector”型,而是“InstrumentedList‘的:

当家长的addChild1方法()我试图测试包含.foo()方法(它应该只是打印’真“)我得到这个错误:

AttributeError: 'InstrumentedList' object has no attribute 'foo' 

显示整个回溯:

Traceback (most recent call last): 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.publisher-3.12.0-py2.4.egg/zope/publisher/publish.py", line 134, in publish 
    result = publication.callObject(request, obj) 
    File "/home/ae/mytests-cms/grokserver/eggs/grok-1.1rc1-py2.4.egg/grok/publication.py", line 89, in callObject 
    return super(ZopePublicationSansProxy, self).callObject(request, ob) 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.app.publication-3.10.2-py2.4.egg/zope/app/publication/zopepublication.py", line 205, in callObject 
    return mapply(ob, request.getPositionalArguments(), request) 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.publisher-3.12.0-py2.4.egg/zope/publisher/publish.py", line 109, in mapply 
    return debug_call(obj, args) 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.publisher-3.12.0-py2.4.egg/zope/publisher/publish.py", line 115, in debug_call 
    return obj(*args) 
    File "/home/ae/mytests-cms/grokserver/eggs/grokcore.view-1.13.2-py2.4.egg/grokcore/view/components.py", line 101, in __call__ 
    return mapply(self.render,(), self.request) 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.publisher-3.12.0-py2.4.egg/zope/publisher/publish.py", line 109, in mapply 
    return debug_call(obj, args) 
    File "/home/ae/mytests-cms/grokserver/eggs/zope.publisher-3.12.0-py2.4.egg/zope/publisher/publish.py", line 115, in debug_call 
    return obj(*args) 
    File "/home/ae/mytests-cms/grokserver/src/grokserver/app.py", line 1575, in render 
    mylibraries.database.tests.Test.runWholeTest() 
    File "/home/ae/mytests-cms/mylibraries/database/tests/Test.py", line 54, in runWholeTest 
    __test() 
    File "/home/ae/mytests-cms/mylibraries/database/tests/Test.py", line 35, in __test 
    parent.addChild1(child) 
    File "/home/ae/mytests-cms/mylibraries/database/tests/Parent.py", line 54, in addChild1 
    print("::addChild1 > Testing .foo method: " + str(self.child1.foo())) 
AttributeError: 'InstrumentedList' object has no attribute 'foo' 
Debug at: http://127.0.0.1:8080/_debug/view/1289342582 

很奇怪......得到正确执行ZepConnector的初始化方法......但是当我尝试使用它,它似乎并没有被ZepConnector .. 。

我做了一对夫妇的更多的测试,但都未果:

在第二次尝试我写道:

class ZepConnector(dict): 
    __emulates__ = set 

但是这甚至使事情变得更糟,因为我得到:

​​

在第三(或第二点二)试试,我虽然......“嗯......如果它说ZepConnector是不是列表,也许说的是家长()不使用列表中的关系,可以帮助...也许说明该collection_class是ZepConnector使得ynnecessary关系中的uselist参数......”

所以我写了:

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

但是,这抛出了令人毛骨悚然的异常说话ab出了场,我不应该看到的和我不希望看到... ...过:-D

AttributeError: 'ZepConnector' object has no attribute '_sa_instance_state' 

我使用的python2.4和SQLAlchemy的0.6.6,刚如果它是相关的。

如果有人有任何想法,指导,辅导...什么...我会很感激你跟我一起分享吧...嗯...我们... ...

预先感谢您!

(如果你已经达到了这一行,你肯定应该有一个“谢谢你”只为您的耐心阅读这个庞大的职位)

+0

我甚至不启动理解你的代码。请减少它,分开你的问题(我确信,有不止一个),并且对每个单独的一个进行最小限度的演示。 – knitti 2010-11-09 20:36:53

+0

@knitti>恩......我不认为我可以在其他问题上分解这个问题。主要问题可以理解为:*“我怎样才能使用我的ZepConnector类作为Parent()和Child()类之间的collection_class?”*感谢您阅读它,尽管 – BorrajaX 2010-11-09 20:47:59

+0

我不确定,但并不完全抛出:-)你能说服包含AttributeError的完整追踪?我特别感兴趣的是,在控制流进入'sqlalchemy.util'之前的一行# – knitti 2010-11-09 21:10:48

回答

1

明白了。

我也问过在SqlAlchemy谷歌组中的同一个问题,我刚刚得到了答案。

http://groups.google.com/group/sqlalchemy/msg/5c8fc09a75fd4fa7

报价:

So this is incorrect - collection_class takes a class or other callable as an argument that will produce an instance of your collection. The ZepConnector source you have below indicates that ZepConnector("VR") is an instance of the collection. You need to use a lambda: there. The other errors you're getting would appear to extend from that (and is also why init is called on ZepConnector - you're calling it yourself).

感谢迈克尔·拜尔(和所有那些试图帮助,甚至通过阅读这样一个巨大的信息的人)

+0

好的,然后冷静。出于兴趣:你试图解决什么问题? – knitti 2010-11-10 00:31:21

+0

@knitti>我无法使用自己的类ZepConnector作为* collection_class *。我不知道是否可以使用从dict(映射对象)扩展来的类来模拟一个set/list的行为,因为在SqlAlchemy的例子中我看到它们使用了一个collection_class类,它们从列表扩展为列表或从字典延伸作为字典的类,但我想从字典中延伸出来的行为作为一个列表...我甚至不知道这是否可行(我认为这是但我不确定) – BorrajaX 2010-11-10 15:00:43

+0

原来我唯一需要做的就是在Parent.py中写入:collection_class = lambda:ZepConnector(“VR”)而不是collection_class = ZepConnector(“VR”) – BorrajaX 2010-11-10 15:04:12