Geometry(POINT)列返回str对象引发AttributeError问题求助
问题排查与解决方案
看起来你遇到的核心问题是:提交事务后,Pin对象的gps属性还是你最初赋值的WKT字符串,而非GeoAlchemy2返回的WKBElement对象,所以调用.data时会触发'str' object has no attribute 'data'的错误。而另一模型能正常运行,大概率是因为它的对象在调用解析方法前,已经被从数据库重新加载过。
下面是具体的排查思路和解决方案:
一、问题根源解析
当你直接把Point.wkt生成的字符串赋值给pin.gps并提交事务后,当前会话中的pin对象并没有自动刷新——它的gps属性仍然是你最初传入的字符串,而非数据库返回的、带有.data属性的WKBElement类型。
另一模型能正常工作,通常是因为:
- 它的对象是通过
session.get()等方式重新从数据库查询出来的,而非直接使用创建后的实例; - 或者该模型调用解析方法时,触发了SQLAlchemy的自动刷新(比如会话配置了
expire_on_commit=True,访问过期对象属性时会自动重新查询)。
二、具体解决方案
方案1:提交事务后手动刷新对象
在提交事务后,调用会话的refresh()方法,强制从数据库重新加载pin的属性,让gps转换成正确的WKBElement类型:
gps = Point(float(data['longitude']), float(data['latitude'])).wkt pin = Pin(gps=gps) with transaction.manager: self.dbsession.add(pin) transaction.commit() self.dbsession.refresh(pin) # 关键:刷新对象,获取数据库中的实际类型 print(pin.as_dict())
方案2:直接使用Shapely Point对象赋值(推荐)
不需要手动转WKT字符串,GeoAlchemy2可以直接处理Shapely的Point对象,自动完成数据库类型转换。这样即使不手动刷新,属性也会是正确的类型:
from shapely.geometry import Point # 直接赋值Point对象,无需手动转WKT gps = Point(float(data['longitude']), float(data['latitude'])) pin = Pin(gps=gps) with transaction.manager: self.dbsession.add(pin) transaction.commit() print(pin.as_dict())
方案3:修改as_dict方法兼容两种类型
如果无法保证gps属性的类型,可以在方法中做兼容判断,同时支持WKT字符串和WKBElement:
def as_dict(self): toret = {} gps_val = self.gps if isinstance(gps_val, str): # 处理WKT字符串 from shapely.wkt import loads as wkt_loads point = wkt_loads(gps_val) else: # 处理WKBElement对象 point = wkb.loads(bytes(gps_val.data)) lat = point.x lon = point.y toret['gps'] = {'lon': lon, 'lat': lat} return toret
三、额外排查建议
- 检查另一模型的调用逻辑:确认它是不是在提交事务后,通过
session.get()重新查询了对象,而非直接使用创建后的实例; - 查看Pyramid会话配置:默认情况下SQLAlchemy会话的
expire_on_commit是True,提交事务后对象会过期,访问属性时会自动刷新。如果你的会话被修改过这个配置,可能导致对象不会自动刷新,需要手动调用refresh()。
内容的提问来源于stack exchange,提问作者wanderingProgrammer
相关产品推荐
相关产品推荐

