2010-11-09 28 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