SQLAlchemy提交对通过__dict__修改的对象的更改 [英] SQLAlchemy commit changes to object modified through __dict__

查看:143
本文介绍了SQLAlchemy提交对通过__dict__修改的对象的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发多人游戏。当我使用清单中的对象时,它应该使用对象属性的值更新用户生物的统计信息。

I am developing a multiplayer game. When I use an object from inventory, it should update the user creature's stats with the values of the attributes of an 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.__dict__[str(attribute.Name)] += attribute.Value

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

现在,由于某种原因,这不能正确完成操作,所以我尝试了:

Now, this doesn't commit things properly for some reason, so I tried:

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

哪个可行,但不是很方便(因为我需要一个大的if语句来更新该生物的不同统计信息)

Which works, but is not very convenient (since I would need a big if statement to updated the different stats of the creature)

所以我尝试了:

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

我在日志中得到 100 ,但是没有任何变化,所以我尝试了:

I get 100 in logs, but no changes, so I tried:

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

在日志中仍然为 100,但没有更改,所以我尝试了:

Still '100' in logs, but no changes, so I tried:

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

其中仍然在日志中写入100,但不将更改提交到数据库。
现在,这很奇怪,因为它的不同之处仅在于工作版本,因为它的最上面是这一行:

Which still writes 100 in the logs, but doesn't commit changes to the database. Now, this is weird, since it only differ by the working version for the fact that it has this line on top:

cur_creature.__dict__['Health'] = 100

摘要:如果我直接修改属性,则提交工作正常。相反,如果我通过类的字典修改属性,则无论以后如何修改,它都不会将更改提交给数据库。

Summary: If I modify an attribute directly, commit works fine. Instead, if I modify an attribute through the class' dictionary, then, no matter how I modify it afterwards, it doesn't commit changes to the db.

任何想法?

预先感谢

更新1:

此外,这会更新数据库中的Health,但不会更新Hunger:

Also, this updates Health in the db, but not Hunger:

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

因此,一般而言,仅访问字典对于属性不是问题,但是通过字典修改属性可以防止对该属性的更改被提交

So just accessing the dictionary is not a problem for attributes in general, but modifying an attribute through the dictionary, prevents the changes to that attributes from being committed.

更新2:

作为临时解决方案,我已覆盖了 __ set_item __(self)在类生物中:

As a temporary fix, I've overridden the function __set_item__(self) in the class Creatures:

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

这样使用对象的新代码为:

So that the new code for 'use object' is:

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)

Update 3:

通过查看答案中的建议,我选择了以下解决方案:

By having a look at the suggestions in the answers, I settled down for this solution:

生物

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


推荐答案

问题不在于SQLAlchemy,而是由于Python的<一种href = http://docs.python.org/2/howto/descriptor.html rel = noreferrer>描述符机制。每个 Column 属性都是一个描述符:这就是SQLAlchemy如何钩属性检索和修改以产生数据库请求。

The problem does not reside in SQLAlchemy but is due to Python's descriptors mechanism. Every Column attribute is a descriptor: this is how SQLAlchemy 'hooks' the attribute retrieval and modification to produce database requests.

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

Let's try with a simpler example:

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 instance 字典并为'desc'设置另一个值,则绕过描述符协议(请参见调用描述符):

However, if you go through a instance dictionary and set another value for 'desc', you bypass the descriptor protocol (see Invoking Descriptors):

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

在这里,我们刚刚创建了一个新的 instance 属性,称为'desc'的值为0。从未调用 Desc .__ set __ 方法,在您的情况下,SQLAlchemy无法获得

Here, we just created a new instance attribute called 'desc' with a value of 0. The Desc.__set__ method was never called, and in your case SQLAlchemy wouldn't get a chance to 'catch' the assignment.

解决方案是使用 setattr ,这完全等同于编写 a.desc

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

这篇关于SQLAlchemy提交对通过__dict__修改的对象的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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