在SqlAlchemy中使用从dict()扩展的自定义集合 [英] Using a custom collection extending from a dict() with SqlAlchemy

查看:168
本文介绍了在SqlAlchemy中使用从dict()扩展的自定义集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用一个自定义集合来连接"(或关联)两个类,但是我无法做到这一点.也许我得到了的整个概念SqlAlchemy自定义集合错误,但是让我解释一下我在做什么(看看是否有人可以给我提示或其他内容)

我有一个Parent类(其中有些人会从其他问题中记住这一点),其中包含几个连接器字段(列表的种类).其中一个连接器将存储类型为" 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框架.

我想普通SqlAlchemy中Parent()类的映射类似于:

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%... erm ... 90%... erm ... 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


比方说,每个Child实例都将具有一个唯一的哈希"字段,可以用作字典中的键(上面的示例与实际情况相去甚远,但是它说明了Child的工作方式以及对于能够创建测试)

现在是我的自定义连接器.我希望它表现为 list set (更像是 set ,尽管我不太介意),但这是一类是从dict继承的.

-------------------- 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 :

在Parent()的addChild1方法中,我尝试测试.foo()方法(该方法应该只显示"True")时出现此错误:

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的 init 方法已正确执行...但是当我尝试使用它时,它似乎并不是ZepConnector ...

我做了更多测试,但都失败了:

第二次尝试中,我写道:

class ZepConnector(dict):
    __emulates__ = set

但这甚至会使情况更糟,因为我得到了:

TypeError: Incompatible collection type: ZepConnector is not list-like

在第三次(或第二点)中,我尝试...好吧……如果说ZepConnector不是列表,也许告诉Parent()不要在关系中使用列表可能会有所帮助...也许说collection_class是一个ZepConnector,使得关系中的uselist参数成为必需的..."

所以我写道:

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

但这引发了一个令人毛骨悚然的异常,谈论一个我不应该看到且我不希望看到的字段... :-D

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

我正在使用Python2.4和SqlAlchemy 0.6.6,以防万一.

如果有人有任何想法,指导,咨询...任何...我非常感谢您与我分享...嗯...我们...

提前谢谢!

(如果您已经达到这条线,您一定要耐心地感谢您阅读这篇大文章)

解决方案

知道了.

我在SqlAlchemy谷歌小组中也问过同样的问题,我刚刚得到了答案.

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

报价:

所以这是不正确的- collection_class上一堂课或 另一个可调用的参数 将产生您的实例 收藏. ZepConnector源 您在下面表示 ZepConnector("VR")是 集合.您需要使用 lambda:在那里.其他错误 你得到的似乎会扩展 由此(也是 init 是 在ZepConnector上呼叫-您是 自己称呼它.)

感谢迈克尔·拜耳(以及所有尝试提供帮助的人,即使阅读了如此丰厚的帖子)

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 SqlAlchemy 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 connectors fields (kind of lists) in it. One of the connectors will store instances of a Child() class whose type is "VR" and the other will store 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" class (and, for purposes of the example, it's method foo() is 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'll 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 maps the Python class to an SqlAlchemy mapper itself and makes it a little bit more "programmer friendly" when using the Grok framework.

I guess The mapping of the Parent() class in regular SqlAlchemy would resemble 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%... erm... 70% 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 SqlAlchemy Custom 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('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 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 for being able to create a test)

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


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 print "True") I get this error:

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

Showing whole traceback:

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

It's strange... The init method of the ZepConnector is properly executed... but when I try to use it, it doesn't seem to be ZepConnector...

I did a couple of more tests, but all unsuccessful:

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 two) 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 ynnecessary 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'

I am using Python2.4 and SqlAlchemy 0.6.6, just in case it's relevant.

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 post)

解决方案

Got it.

I had also asked the same question in the SqlAlchemy google group and I just got an answer.

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

Quote:

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).

Thanks to Michael Bayer (and to all the people that tried to help, even by reading such a humongous post)

这篇关于在SqlAlchemy中使用从dict()扩展的自定义集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆