On Mon, Aug 7, 2017 at 10:45 AM, Ruben Di Battista <rubendibatti...@gmail.com> wrote: > Hello, > I have a tables with two ForeignKeys. When I remove the relation on one > side, SQLAlchemy sets to 'NULL' the related ForeignKey, but the related row > is not considered orphaned since it hase still the other ForeignKey.
an object is considered orphaned when *any* relationships that refer to it with "delete-orphan" set are non-present as parents. There is a flag "legacy_is_orphan" which enables the pre-0.8 "orphan" concept which is what you describe: http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html?highlight=legacy_is_orphan#sqlalchemy.orm.mapper.params.legacy_is_orphan Is > there a way to make SQLAlchemy fulfill the `orphan-delete' cascade when only > one of the multiple ForeignKeys are removed? If I set them as NOT NULL it > will cause an Error. in this case the specific issue is that you are making multi-state mutations to the sensor.readings collection before any of the objects are associated with the Session, leading it to miss the specific event hook that accommodates for the expunging of pending items removed from the collection; works as: s = session = Session(e) sensor = Sensor('Pressure Sensor') room = Room('bedroom') readings = sensor.read(room) session.add(sensor) accepted_readings = [] for r in readings: if r.voltage > 10: accepted_readings.append(r) sensor.readings = accepted_readings session.commit() issue https://bitbucket.org/zzzeek/sqlalchemy/issues/4040/expunge-pending-orphans-on-flush-that is added to attempt to accommodate for the specific case of late-added pending "orphan". The complexity is that we include that the following should always try to INSERT and then fail, rather than silently ignore the request: obj = Reading() session.add(obj) session.commit() so in this case we need to distinguish between the fact that the Reading was added and removed via cascade and not by explicit session.add(). orphaning is only supposed to occur when the de-association happens, not if the object was never associated. > > MWE: > > from sqlalchemy import Column, Integer, String, DateTime, Float,\ > ForeignKey > from sqlalchemy.orm import relationship, sessionmaker > from sqlalchemy.ext.declarative import declarative_base > from pyggdrasill.sql.schema import connect_db > > from datetime import datetime > > Base = declarative_base() > > > class Sensor(Base): > __tablename__ = 'sensor' > > id = Column(Integer, primary_key=True) > name = Column(String(150)) > readings = relationship("Reading", backref='sensor', > cascade='all, delete-orphan') > > def __init__(self, name): > self.name = name > > def read(self, room): > return [ > Reading( > self, > room, > datetime.now(), > 10.0), > > Reading( > self, > room, > datetime.now(), > 20.0), > > Reading( > self, > room, > datetime.now(), > 30.0) > ] > > > class Room(Base): > __tablename__ = 'room' > > id = Column(Integer, primary_key=True) > name = Column(String(150)) > readings = relationship("Reading", backref='room', > cascade='all, delete-orphan') > > def __init__(self, name): > self.name = name > > > class Reading(Base): > __tablename__ = 'reading' > > id = Column(Integer, primary_key=True) > date = Column(DateTime) > voltage = Column(Float) > > sensor_id = Column(Integer, ForeignKey('sensor.id')) > room_id = Column(Integer, ForeignKey('room.id')) > > def __init__(self, sensor, room, date, voltage): > self.sensor = sensor > self.room = room > self.date = date > self.voltage = voltage > > def __repr__(self): > return '<Read {} {}>'.format(self.date, self.voltage) > > > if __name__ == '__main__': > > db = connect_db( > username='pygg', > password='albero della vita', > db_name='pyggdrasill', > echo=False) > > Base.metadata.create_all(db) > S = sessionmaker(db) > session = S() > > sensor = Sensor('Pressure Sensor') > room = Room('bedroom') > readings = sensor.read(room) > > accepted_readings = [] > for r in readings: > if r.voltage > 10: > accepted_readings.append(r) > > sensor.readings = accepted_readings > session.add(sensor) > session.commit() > > print(len(accepted_readings)) > print(len(session.query(Reading).all())) > > > > > -- > SQLAlchemy - > The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at https://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.