On 05/18/2017 09:20 AM, Jaco Breitenbach wrote:
Hi Mike,
Thank you very much. I've managed to get it working after a fashion.
Unfortunately this technique seems to interfere with the automatic
clean-up of the secondary (mapping) table in the case of a many-to-many
relationship. In the example below I have a contact_group table and
user table and a many-to-many relationship set up via a
contact_group_user_map table. Since I've used backref in defining the
relationship, I expect the row in contact_group_user_map to
automatically be deleted when I do session.delete(userObj). This works
perfectly as long as the before_flush handler is disabled. If I
intercept the delete of userObj and turn it into an update, the delete
from contact_group_user_map no longer happens.
well you are turning delete into update, so if you also want to mutate
that collection, you need to do that manually. should be easy w/ the
backref, e.g.:
my_soft_deleted_instance.users = []
I've noticed that the
list of objects in session.deleted which is passed to the before_flush
handler does not include the contact_group_user_map entity, so I guess
the determination to delete the row from contact_group_user_map takes
place after the before_flush handler returns. Do you have any advice on
resolving this issue?
from sqlalchemy import create_engine, event, BigInteger, Column,
DateTime, Float, ForeignKey, Index, Integer, String, text
from sqlalchemy.orm import attributes, relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy_repr import PrettyRepresentableBase
Base = declarative_base(cls=PrettyRepresentableBase)
metadata = Base.metadata
engine = None
Session = None
def handle__before_flush(session, flush_context, instances):
print("handle__before_flush()")
for instance in session.deleted:
print(instance)
if not attributes.instance_state(instance).has_identity:
continue
if not hasattr(instance, '_deleted'):
continue
# instance._deleted = True
# session.add(instance)
def getSession(conn):
global engine, Session
if engine is None or Session is None:
engine = create_engine(conn, echo=True)
Session = sessionmaker(bind=engine, query_cls=SoftDeleteQuery)
event.listen(Session, 'before_flush', handle__before_flush)
return Session()
class ContactGroup(Base):
__tablename__ = 'contact_group'
id = Column(BigInteger, primary_key=True)
_deleted = Column(Integer, nullable=False, server_default=text("'0'"))
name = Column(String(200), nullable=False)
description = Column(String(1000))
users = relationship("User", secondary="contact_group_user_map",
backref="contact_groups")
class ContactGroupUserMap(Base):
__tablename__ = 'contact_group_user_map'
id = Column(BigInteger, primary_key=True)
contact_group_id = Column(ForeignKey(u'contact_group.id'),
nullable=False, index=True)
user_id = Column(ForeignKey(u'user.id'), nullable=False, index=True)
contact_group = relationship(u'ContactGroup')
user = relationship(u'User')
class User(Base):
__tablename__ = 'user'
id = Column(BigInteger, primary_key=True)
_deleted = Column(Integer, nullable=False, server_default=text("'0'"))
username = Column(String(200), nullable=False, unique=True)
password = Column(String(1000))
Thank you and kind regards,
Jaco
On Wednesday, 17 May 2017 23:30:25 UTC+1, Mike Bayer wrote:
On 05/17/2017 05:25 PM, Jaco Breitenbach wrote:
> Thank you, Jonathan, that wiki example has helped a lot. I've
extended
> it a bit to also add the extra condition to joined tables.
>
> I want to take it one step further now and automatically convert
> session.delete()s into update statements (set is_deleted = true)
instead
> of delete statements. I've experimented with a custom
SessionExtension,
don't use "Extensions", use the event system:
http://docs.sqlalchemy.org/en/rel_1_1/orm/events.html?highlight=before_flush#sqlalchemy.orm.events.SessionEvents.before_flush
<http://docs.sqlalchemy.org/en/rel_1_1/orm/events.html?highlight=before_flush#sqlalchemy.orm.events.SessionEvents.before_flush>
> session.deleted.pop() # Remove the instance
from the
> list of entities to be deleted
ouch...that's not going to do anything. everything in Python is
mutable and it's kind of hard to make every collection everywhere that
only is a view of things immutable (though this is custom collection,
could be made immutable with a flag or something). To undo the
delete,
add the object back, session.add(object).
>
>
> Thank you and kind regards,
> Jaco
>
>
> On Tuesday, 16 May 2017 17:20:12 UTC+1, Jonathan Vanasco wrote:
>
> The FAQ
>
http://docs.sqlalchemy.org/en/latest/faq/sessions.html#how-do-i-make-a-query-that-always-adds-a-certain-filter-to-every-query
<http://docs.sqlalchemy.org/en/latest/faq/sessions.html#how-do-i-make-a-query-that-always-adds-a-certain-filter-to-every-query>
>
<http://docs.sqlalchemy.org/en/latest/faq/sessions.html#how-do-i-make-a-query-that-always-adds-a-certain-filter-to-every-query
<http://docs.sqlalchemy.org/en/latest/faq/sessions.html#how-do-i-make-a-query-that-always-adds-a-certain-filter-to-every-query>>
>
> points to an example in the wiki:
>
https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery>
>
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery>>
>
>
> --
> 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
<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+...@googlegroups.com <javascript:>
> <mailto:sqlalchemy+unsubscr...@googlegroups.com <javascript:>>.
> To post to this group, send email to sqlal...@googlegroups.com
<javascript:>
> <mailto:sqlal...@googlegroups.com <javascript:>>.
> Visit this group at https://groups.google.com/group/sqlalchemy
<https://groups.google.com/group/sqlalchemy>.
> For more options, visit https://groups.google.com/d/optout
<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
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto: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.