SQLAlchemy会话没有添加我的一些对象

时间:2017-07-12 17:45:48

标签: python mysql session sqlalchemy

我使用SQLAlchemy中的声明式样式创建了一些映射对象。我有一个名为ThermafuserReading的映射,它有一个由Time_stamp列组成的组合主键,它是DateTime和ThermafuserId列,它是一个Integer,也可以作为另一个名为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(读数)添加此类会话列表

这是从列表读数打印的一些示例元素:

<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(读数),例如这就是会议内部的内容:

<ThermafuserReading(thermafuserId = '21', timestamp = '2017-01-01 00:00:00')>

我知道会话跟踪具有相同主键的对象,因此在sesssion中只插入这样的对象的一个​​实例,但在这种情况下,每个实例的主键(thermafuserId,timestamp)是不同的。我不知道为什么会话只是添加我的列表的最后一个元素而忽略了其他元素。

有什么想法吗?

修改

我一直在做一些测试,并找出了为什么只将列表的最后一个元素添加到会话中的原因。问题在于列表读数中每个对象的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))

1 个答案:

答案 0 :(得分:0)

正如评论中提到的univerio。我做错了是使用copy.copy来复制我的映射对象的实例,这是在弄乱_sa_instance_state。解决方案是创建一个&#34; ad hoc&#34;我的实例的复制功能。这是我使用的复制功能,这确实解决了这个问题。

def copy_sqla_object(obj, omit_fk=True):
    """Given an SQLAlchemy object, creates a new object (FOR WHICH THE OBJECT
    MUST SUPPORT CREATION USING __init__() WITH NO PARAMETERS), and copies
    across all attributes, omitting PKs, FKs (by default), and relationship
    attributes."""
    cls = type(obj)
    mapper = class_mapper(cls)
    newobj = cls()  # not: cls.__new__(cls)
    pk_keys = set([c.key for c in mapper.primary_key])
    rel_keys = set([c.key for c in mapper.relationships])
    prohibited = pk_keys | rel_keys
    if omit_fk:
        fk_keys = set([c.key for c in mapper.columns if c.foreign_keys])
        prohibited = prohibited | fk_keys
    for k in [p.key for p in mapper.iterate_properties if p.key not in prohibited]:
        try:
            value = getattr(obj, k)
            setattr(newobj, k, value)
        except AttributeError:
            pass
    return newobj

您可以在

中看到有关此问题的更详细讨论

https://groups.google.com/forum/#!topic/sqlalchemy/HVSxndh23m0