SQLAlchemy提交对通过__dict__修改的对象的更改

时间:2013-06-09 20:42:55

标签: python sqlalchemy commit

我正在开发一款多人游戏。当我使用库存中的对象时,它应该使用对象属性的值更新用户生物的统计数据。

这是我的代码:

try:
    obj = self._get_obj_by_id(self.query['ObjectID']).first()

    # Get user's current creature
    cur_creature = self.user.get_current_creature()

    # Applying object attributes to user attributes
    for attribute in obj.attributes:
        cur_creature.__dict__[str(attribute.Name)] += attribute.Value

    dbObjs.session.commit()
except (KeyError, AttributeError) as err:
    self.query_failed(err)

现在,由于某些原因,这并没有正确提交,所以我尝试了:

cur_creature.Health  = 100
logging.warning(cur_creature.Health)
dbObjs.session.commit()

哪个有效,但不是很方便(因为我需要一个大的if语句来更新该生物的不同统计数据)

所以我试过了:

cur_creature.__dict__['Health'] = 100
logging.warning(cur_creature.Health)
dbObjs.session.commit()

我在日志中获得100,但没有更改,所以我尝试了:

cur_creature.__dict__['Health'] = 100
cur_creature.Health  = cur_creature.__dict__['Health']
logging.warning(cur_creature.Health)
dbObjs.session.commit()

日志中仍为'100',但没有变化,所以我尝试了:

cur_creature.__dict__['Health'] = 100
cur_creature.Health  = 100
logging.warning(cur_creature.Health)
dbObjs.session.commit()

仍然在日志中写入100,但不提交对数据库的更改。 现在,这很奇怪,因为它只有不同的工作版本,因为它有这条线在顶部:

cur_creature.__dict__['Health'] = 100

摘要:如果我直接修改属性,则提交正常。相反,如果我通过类'字典​​修改属性,那么,无论我之后如何修改它,它都不会提交对数据库的更改。

有什么想法吗?

提前致谢

更新1:

此外,这会更新db中的Health,但不会更新Hunger:

cur_creature.__dict__['Hunger'] = 0
cur_creature.Health  = 100
cur_creature.Hunger = 0
logging.warning(cur_creature.Health)
dbObjs.session.commit()

因此,一般来说,访问字典对于属性来说不是问题,但是通过字典修改属性会阻止对属性的更改提交。

更新2:

作为临时修复,我已覆盖了课程__set_item__(self)中的Creatures函数:

def __setitem__(self, key, value):
    if key == "Health":
       self.Health = value
    elif key == "Hunger":
       self.Hunger = value

因此'use object'的新代码是:

try:
    obj = self._get_obj_by_id(self.query['ObjectID']).first()

    # Get user's current creature
    cur_creature = self.user.get_current_creature()

    # Applying object attributes to user attributes
    for attribute in obj.attributes:
        cur_creature[str(attribute.Name)] += attribute.Value

    dbObjs.session.commit()
except (KeyError, AttributeError) as err:
    self.query_failed(err)

更新3:

通过查看答案中的建议,我找到了解决方案:

Creatures

def __setitem__(self, key, value):
    if key in self.__dict__:
       setattr(self, key, value)
    else:
       raise KeyError(key)

在另一种方法中

 # Applying object attributes to user attributes
 for attribute in obj.attributes:
     cur_creature[str(attribute.Name)] += attribute.Value

2 个答案:

答案 0 :(得分:9)

问题不在于SQLAlchemy,而是由于Python的descriptors mechanism。每个Column属性都是一个描述符:这就是SQLAlchemy“挂钩”属性检索和修改以产生数据库请求的方式。

让我们尝试一个更简单的例子:

class Desc(object):
    def __get__(self, obj, type=None):
        print '__get__'
    def __set__(self, obj, value):
        print '__set__'

class A(object):
    desc = Desc()

a = A()
a.desc                  # prints '__get__'
a.desc = 2              # prints '__set__'

但是,如果您浏览a 实例字典并为'desc'设置另一个值,则绕过描述符协议(请参阅Invoking Descriptors):

a.__dict__['desc'] = 0  # Does not print anything !

在这里,我们刚刚创建了一个名为'desc'的新实例属性,其值为0.从未调用Desc.__set__方法,在您的情况下,SQLAlchemy不会有机会“抓住”作业。

解决方案是使用setattr,这与写a.desc完全相同:

setattr(a, 'desc', 1)   # Prints '__set__'

答案 1 :(得分:2)

请勿使用__dict__。使用getattrsetattr按名称修改属性:

for attribute in obj.attributes:
    setattr(cur_creature,str(attribute.Name), getattr(cur_creature,str(attribute.Name)) + attribute.Value)

更多信息:

setattr

getattr