Skip to content

Instantly share code, notes, and snippets.

@tspng
Created July 27, 2021 10:40
Show Gist options
  • Save tspng/e6f8cbb755c335546d2602cc89b7d16e to your computer and use it in GitHub Desktop.
Save tspng/e6f8cbb755c335546d2602cc89b7d16e to your computer and use it in GitHub Desktop.
SQLAlchemy Many-to-One delete with backref testcase
import unittest
from sqlalchemy import Column, Integer, ForeignKey, create_engine, event
from sqlalchemy.orm import backref, relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import IntegrityError
from sqlalchemy.engine import Engine
Base = declarative_base()
engine = create_engine('sqlite://')
Session = sessionmaker(bind=engine)
session = Session()
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
class Image(Base):
__tablename__ = 'image'
id = Column(Integer, primary_key=True)
def __repr__(self):
return f"<Image(id={self.id})>"
class ThingWithImages(Base):
__tablename__ = 'thing_with_images'
id = Column(Integer, primary_key=True)
image_id = Column(Integer, ForeignKey("image.id"))
logo_id = Column(Integer, ForeignKey("image.id"))
image = relationship("Image", foreign_keys=[image_id], backref="things")
logo = relationship("Image", foreign_keys=[logo_id])
def __repr__(self):
return f"<ThingWithImages(id={self.id}, image_id={self.image_id}, logo_id={self.logo_id})>"
class ManyToOneDeletionTestCase(unittest.TestCase):
def setUp(self):
Base.metadata.create_all(engine)
self.image = Image()
self.logo = Image()
self.thing1 = ThingWithImages(image=self.image, logo=self.logo)
self.thing2 = ThingWithImages(image=self.image, logo=self.logo)
session.add_all([self.thing1, self.thing2])
session.commit()
def tearDown(self):
Base.metadata.drop_all(engine)
def test_relations(self):
self.assertEqual(self.image.id, self.thing1.image_id)
self.assertEqual(self.image.id, self.thing2.image_id)
def test_delete_logo_without_backref(self):
self.assertEqual(session.query(Image).count(), 2)
session.delete(self.logo)
self.assertRaises(IntegrityError, session.commit)
session.rollback()
self.assertEqual(session.query(Image).count(), 2)
def test_delete_image_with_backref(self):
self.assertEqual(session.query(Image).count(), 2)
session.delete(self.image)
# Assumed the following:
# self.assertRaises(IntegrityError, session.commit)
# Different behavior when deleting with backref on relationship?
self.assertEqual(session.query(Image).count(), 1)
self.assertIsNone(self.thing1.image_id)
self.assertIsNone(self.thing1.image)
self.assertIsNone(self.thing2.image_id)
self.assertIsNone(self.thing2.image)
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment