【发布时间】:2017-12-17 06:42:04
【问题描述】:
我使用 SQLAlchemy 中的声明式样式创建了一些映射对象。我有一个名为 ThermafuserReading 的映射,它有一个由 Time_stamp 列组成的组合主键,它是 DateTime 和 ThermafuserId 列,它是一个整数,并且还充当另一个名为 Thermafuser 的表的外键。这是类的定义
class ThermafuserReading(Base):
"""Class to map to the Thermafuser Readings table in the HVAC DB"""
__tablename__ = 'Thermafuser_Reading'
_timestamp = Column('Time_stamp', DateTime, primary_key = True)
_thermafuserId = Column('ThermafuserId', Integer, ForeignKey("Thermafuser.ThermafuserId"), primary_key = True)
_roomOccupied = Column('RoomOccupied', Boolean)
_zoneTemperature = Column('ZoneTemperature', Float)
_supplyAir = Column('SupplyAir', Float, nullable=True)
_airflowFeedback = Column('AirflowFeedback', Float, nullable=True)
_CO2Input = Column('CO2Input', Float, nullable=True)
_maxAirflow = Column('MaxAirflow', Float, nullable=True)
_minAirflow = Column('MinAirflow', Float, nullable=True)
_unoccupiedHeatingSetpoint = Column('UnoccupiedHeatingSetpoint', Float, nullable=True)
_unoccupiedCoolingSetpoint = Column('UnoccupiedCoolingSetpoint', Float, nullable=True)
_occupiedCoolingSetpoint = Column('OccupiedCoolingSetpoint', Float, nullable=True)
_occupiedHeatingSetpoint = Column('OccupiedHeatingSetpoint', Float, nullable=True)
_terminalLoad = Column('TerminalLoad', Float, nullable=True)
#Relationship between Thermafuser Reading and Thermafuser
_thermafuser = relationship("Thermafuser", back_populates = "_thermafuserReadings", cascade = "all, delete-orphan", single_parent = True)
我正在通过以下方式创建会话
sqlengine = sqlalchemy.create_engine("mysql+mysqldb://user:password@localhost:3306/HVAC")
Session = sessionmaker(bind=sqlengine)
session = Session()
在我的代码中,我正在创建一个名为“Thermafuser Readings 读数”的列表,并通过 session.add_all(readings) 将此类列表添加到会话中
这是从列表读数中打印出来的一些示例元素:
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:30:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:35:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:40:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:45:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:50:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2016-12-31 23:55:00')>
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-01 00:00:00')>
问题是会话只保留此列表中的最后一项,即使我做了 session.add_all(readings) 例如这是会话内部的内容:
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-01 00:00:00')>
我知道会话会跟踪具有相同主键的对象,因此在会话中仅插入此类对象的一个实例,但在这种情况下,主键(thermafuserId、时间戳)在每个实例中都不同。我不知道为什么会话只添加了我列表的最后一个元素而忽略了其他元素。
有什么想法吗?
编辑:
我一直在做一些测试,并找出了为什么只将列表的最后一个元素添加到会话中的原因。问题在于我的列表 readings 中每个对象的 identity_key。这是我用于测试的代码:
for reading in readings:
print(reading, mapper.identity_key_from_instance(reading))
这是一些结果
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:15:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:20:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:25:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:30:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:35:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:40:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:45:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:50:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-14 23:55:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-15 00:00:00')> (<class 'hvacDBMapping.ThermafuserReading'>, (datetime.datetime(2017, 1, 15, 0, 0), 21))
如您所见,函数 sqlalchemy.orm.util.identity_key_from_instance() 没有为我的日期时间对象正确创建身份密钥。
有人可以帮我解释一下原因吗?
编辑
这是说明问题的简化代码。此代码中没有与数据库的连接。首次出现此问题的代码涉及更多,发布它只会造成混乱,但此代码会重现错误。
Session = sessionmaker()
session = Session()
mapper = inspect(ThermafuserReading)
#Open the csv file
csvFilePath = "/Users/davidlaredorazo/Box Sync/Data/Zone4/1C1A/1C1A 2016-12-31.csv"
with open(csvFilePath, 'r') as csvfile:
reader = csv.reader(csvfile)
componentId = 1
count = 0
reading = ThermafuserReading(None, componentId)
for row in reader:
if count == 0:
count += 1
continue
#print(row)
timestamp = parse(row[0], None, ignoretz = True)
reading.timestamp = timestamp
new_object = copy.copy(reading)
new_object.timestamp = timestamp
print(new_object, mapper.identity_key_from_instance(new_object))
session.add(new_object)
print("new elements")
for new in session.new:
print(new, mapper.identity_key_from_instance(new_object))
【问题讨论】:
-
您需要在创建这些实例的位置发布代码。 (您确定您不只是创建一个实例并将同一个实例添加到列表中吗?)以及您如何打印这些实例。 (据我们所知,您正在打印
timestamp = <completely different field than self._timestamp>。) -
感谢您的评论。我刚刚发布了一个重现错误的代码 sn-p。我确信我不会只创建一个实例,因为我正在使用 copy.copy 复制实例。
-
好吧,那是你的问题。您正在使用
copy弄乱 SQLAlchemy 内部结构。不要那样做。如果你真的想要,你需要将_sa_instance_state排除在被复制之外。 -
那么在会话中插入对象的正确方法是什么。问题是我创建了我想要插入的对象的单个实例,然后我修改它的属性并复制该实例并将其插入会话中。那么正确的方法是什么?感谢您的评论
-
不要使用
copy。手动创建ThermafuserReading实例并自己复制属性。
标签: python mysql session sqlalchemy