Hello! I am new to sqlalchemy and was hoping to get some insight on the following issue:
import sqlalchemy
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.schema import ForeignKeyConstraint
Session = sessionmaker()
Base = declarative_base()
class Album(Base):
__tablename__ = "albums"
artist = Column(String, nullable=False, primary_key=True)
title = Column(String, nullable=False, primary_key=True)
year = Column(Integer, nullable=False, primary_key=True)
tracks = relationship(
"Track", back_populates="_album_obj", cascade="all, delete-orphan"
)
def __init__(self, artist: str, title: str, year: int):
self.artist = artist
self.title = title
self.year = year
class Track(Base): # noqa: WPS230
__tablename__ = "tracks"
# track_num + Album = unique track
track_num = Column(Integer, nullable=False, primary_key=True, autoincrement=False)
_albumartist = Column(String, nullable=False, primary_key=True)
_album = Column(String, nullable=False, primary_key=True)
_year = Column(Integer, nullable=False, primary_key=True, autoincrement=False)
artist = Column(String, nullable=False, default="")
title = Column(String, nullable=False, default="")
_album_obj = relationship("Album", back_populates="tracks")
__table_args__ = (
ForeignKeyConstraint(
[_albumartist, _album, _year], # type: ignore
[Album.artist, Album.title, Album.year],
),
)
@hybrid_property
def album(self) -> str:
return self._album_obj.title
@hybrid_property
def albumartist(self) -> str:
return self._album_obj.artist
@hybrid_property
def year(self) -> int:
return self._album_obj.year
def __init__(self, album: str, albumartist: str, track_num: int, year: int):
self._album_obj = Album(artist=albumartist, title=album, year=year)
self.track_num = track_num
# self._album = album
# self._albumartist = albumartist
# self._year = year
engine = sqlalchemy.create_engine("sqlite:///:memory:", echo=True)
Session.configure(bind=engine)
Base.metadata.create_all(engine) # creates tables
session = Session()
track1 = Track(album="tpab", albumartist="K.dot", track_num=1, year=2015)
track2 = Track(album="tpab", albumartist="K.dot", track_num=1, year=2015)
session.merge(track1)
session.merge(track2)
session.commit()
When executing the above code, I get
Traceback (most recent call last):
File "test_merge.py", line 81, in <module>
session.commit()
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1042, in commit
self.transaction.commit()
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 504, in commit
self._prepare_impl()
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 483, in _prepare_impl
self.session.flush()
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2523, in flush
self._flush(objects)
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2664, in _flush
transaction.rollback(_capture_exception=True)
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__
compat.raise_(
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 182, in raise_
raise exception
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2624, in _flush
flush_context.execute()
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 422, in execute
rec.execute(self)
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 586, in execute
persistence.save_obj(
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 205, in save_obj
for (
File "/home/jacob/src/moe/.venv/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py", line 405, in _organize_states_for_save
raise orm_exc.FlushError(
sqlalchemy.orm.exc.FlushError: New instance <Track at 0x7f72ccd17f70> with identity key (<class '__main__.Track'>, (1, 'K.dot', 'tpab', 2015), None) conflicts with persistent instance <Track at 0x7f72ccc901c0>
But, If I uncomment
# self._album = album
# self._albumartist = albumartist
# self._year = year
The code works fine. I don't quite understand why the above code is needed, and happened to add it just by random testing to see if I could pin down the error. I think
the section in the docs on merge tips is the closet thing I could find, but I can't quite understand if the same thing there is happening here. Any insight would be greatly appreciated!