在介绍SQLAlchemy更新操作之前,先来思考一下下面的问题:
class Test(object):
def __init__(self):
self.a = ""
self.b = ""
if __name__ == "__main__":
obj1 = Test()
obj2 = Test()
obj2.a = "xixi"
obj2.b = "haha"
如何在不改变obj1
对象的内存地址的情况下将obj2
对象的属性值全部赋给obj1
对象?
你可能想到用__dict__
这个魔术方法来实现上面的需求:
if __name__ == "__main__":
obj1 = Test()
obj2 = Test()
obj2.a = "xixi"
obj2.b = "haha"
print(obj1.__dict__)
for key in obj2.__dict__:
setattr(obj1, key, getattr(obj2, key))
完美! 了解了上述方式之后我们进入文章的主题。
class HttpProxyDao(object):
...省略非重点
def update(self, update_obj):
try:
object_host = update_obj.ip
object_port = update_obj.port
proxyobj = self.session.query(HttpProxy).filter(HttpProxy.ip.like(object_host),
HttpProxy.port.like(object_port)).one_or_none()
if proxyobj:
for key in update_obj.__dict__:
# setattr(proxyobj, key, update_obj.__dict__[key])
# proxyobj.__setattr__(key, update_obj.__dict__[key]) # 都行
setattr(proxyobj, key, getattr(update_obj, key))
self.session.commit()
return proxyobj
except Exception as e_update:
print("e_update:", e_update)
return None
我们利用下面的语句测试一下:
if __name__ == "__main__":
obj = HttpProxyDao()
httpobj = HttpProxy()
httpobj.ip = "963"
httpobj.port = "123"
httpobj.type = "test"
httpobj.speed = "966"
rest = obj.update(httpobj)
print(rest)
运行之后,惊讶的发现数据库里面并没有任何的变化。 也是是说,刚才写的更新方法压根没起到更新的作用。
怎么回事呢? 写一些语句辅助调试一下:
def update(self, update_obj):
try:
object_host = update_obj.ip
object_port = update_obj.port
proxyobj = self.session.query(HttpProxy).filter(HttpProxy.ip.like(object_host),
HttpProxy.port.like(object_port)).one_or_none()
if proxyobj:
print(proxyobj.__dict__) # 调试
for key in update_obj.__dict__:
setattr(proxyobj, key, getattr(update_obj, key))
print(proxyobj.__dict__) # 调试
self.session.commit()
return proxyobj
except Exception as e_update:
print("e_update:", e_update)
return None
运行结果:
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x0000014993B3D208>, 'survival_time': None, 'speed': None, 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': '', 'server_address': None, 'country': None}
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x0000014993AD7048>, 'survival_time': None, 'speed': '966', 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': 'test', 'server_address': None, 'country': None}
<data_acquisition.data_acquisition.domain.HttpProxy.HttpProxy object at 0x0000014993B3D1D0>
我们发现,明明type,speed这两个属性的值都已经改变,但为啥还是没能修改呢?
仔细看一下会发现,虽然对象的名称都一样,但是内存地址却是不一样的。所以我们判定,proxyobj对象中一定有个属性,它将对象的内存地址给修改了。我们接着将属性的名称打印出来看一下:
def update(self, update_obj):
try:
object_host = update_obj.ip
object_port = update_obj.port
proxyobj = self.session.query(HttpProxy).filter(HttpProxy.ip.like(object_host),
HttpProxy.port.like(object_port)).one_or_none()
if proxyobj:
for key in update_obj.__dict__:
print(key)
setattr(proxyobj, key, getattr(update_obj, key))
print(proxyobj.ip, "更新成功")
self.session.commit()
return proxyobj
except Exception as e_update:
print("e_update:", e_update)
return None
执行结果:
_sa_instance_state
ip
port
type
speed
果然,_sa_instance_state
是个什么玩意?? 进一步,带着好奇心我们查看一下:
if proxyobj:
print(proxyobj.__dict__)
for key in update_obj.__dict__:
print(getattr(proxyobj, '_sa_instance_state'))
setattr(proxyobj, key, getattr(update_obj, key))
print(proxyobj.__dict__)
self.session.commit()
return proxyobj
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x000002437233D278>, 'survival_time': None, 'speed': None, 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': '', 'server_address': None, 'country': None}
<sqlalchemy.orm.state.InstanceState object at 0x000002437233D278>
<sqlalchemy.orm.state.InstanceState object at 0x00000243722D60F0>
<sqlalchemy.orm.state.InstanceState object at 0x00000243722D60F0>
<sqlalchemy.orm.state.InstanceState object at 0x00000243722D60F0>
<sqlalchemy.orm.state.InstanceState object at 0x00000243722D60F0>
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x00000243722D60F0>, 'survival_time': None, 'speed': '966', 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': 'test', 'server_address': None, 'country': None}
<data_acquisition.data_acquisition.domain.HttpProxy.HttpProxy object at 0x000002437233D240>
第一次迭代,_sa_instance_state
的值是proxyobj
对象的内存地址,后面几次就被修改了。
使用猜想_sa_instance_state
属性应该是proxyobj
对象的内存地址。
使用,现在我们只要过滤掉这个属性,就可以完成操作。我们接着修改代码:
def update(self, update_obj):
try:
object_host = update_obj.ip
object_port = update_obj.port
proxyobj = self.session.query(HttpProxy).filter(HttpProxy.ip.like(object_host),
HttpProxy.port.like(object_port)).one_or_none()
if proxyobj:
print(proxyobj.__dict__)
for key in update_obj.__dict__:
if key == '_sa_instance_state':
continue
setattr(proxyobj, key, getattr(update_obj, key))
print(proxyobj.__dict__)
self.session.commit()
return proxyobj
except Exception as e_update:
print("e_update:", e_update)
return None
运行:
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x000001DEC414D2B0>, 'survival_time': None, 'speed': None, 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': '', 'server_address': None, 'country': None}
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x000001DEC414D2B0>, 'survival_time': None, 'speed': '966', 'anonymity': None, 'port': '123', 'ip': '963', 'verify_time': None, 'connection_time': None, 'type': 'test', 'server_address': None, 'country': None}
<data_acquisition.data_acquisition.domain.HttpProxy.HttpProxy object at 0x000001DEC414D278>
好了,内存地址没变。查看数据库中的值也修改成功了。
这里的坑是_sa_instance_state
这个属性,其实这个属性在一开始的那个例子中是不存在的,所以在遇到这种问题的时候,如果发现结果不对劲,还是将key(属性值)打印出来看一下。