On Friday, January 12, 2018 at 10:54:49 AM UTC+1, Simon King wrote:
>
> If I understand your code correctly, scheduler.propagate() creates a 
> large number of Passage instances, and you only want a small subset of 
> them to be added to the database. Is that correct? 
>

Correct!
 

> I would guess that the passages are getting added to the session 
> because you are setting their 'satellite' property to point to a 
> Satellite which is already in the database. This then causes the 
> passages to be added to the session due to the default cascade rules 
> on the relationship 
> (http://docs.sqlalchemy.org/en/latest/orm/cascades.html). 
>
> If that really is the case, you can change the cascade rules for that 
> relationship, and then you'll probably need to explicitly add the 
> passages you want to *keep* to the session instead. 
>
> Hope that helps, 
>
> Simon 
>

Dear Simon, 
thank you. That was the case. Modifying the cascade disabling the backref 
cascade does not load in the DB the passages at propagation time. 

But now, when I manually add the subset of passages after the optimization, 
I get a: 

InvalidRequestError: Can't attach instance <Satellite at 0x108c4dfd0>; 
another instance with key [...] is already present in this section. 

So, I suppose that disabling the backref cascade now SQLAlchemy is not 
capable anymore to recognize the already loaded Satellite objects...

Should I maybe merge somewhere?
 

>
> On Fri, Jan 12, 2018 at 2:10 AM, Mike Bayer <mik...@zzzcomputing.com 
> <javascript:>> wrote: 
> > I can't give you much detail except to say the unique object recipe is 
> > doing an .add() when it finds an identity that isn't taken, if you 
> > don't want those persisted then take out the part of the recipe doing 
> > add().  However, you'd need to alter the recipe further such that if 
> > the program asks for that same identity again which you didn't want to 
> > flush to the DB, and you'd like to use the same object, you need to 
> > pull that from some kind of local dictionary of "pending" objects with 
> > those identities, if that makes sense. 
> > 
> > the second email with the after_attach thing implies you are already 
> > adding an object to the Session. 
> > 
> > Neither of these code examples show example of use, where you are 
> > doing things that make objects and you'd like them to not be 
> > persisted.   If you need to create unique objects in memory without 
> > persisting, you just need to store them in some dictionary that sets 
> > up the in-memory uniqueness you are looking for. 
> > 
> > 
> > 
> > On Thu, Jan 11, 2018 at 11:37 AM, Ruben Di Battista 
> > <rubendi...@gmail.com <javascript:>> wrote: 
> >> Last copy paste went wrong. 
> >> 
> >> The uniqueness is ensured by: 
> >> 
> >> @event.listens_for(orm.session.Session, "after_attach") 
> >> def after_attach(session, instance): 
> >>     # when ConstrainedSatellite objects are attached to a Session, 
> >>     # figure out if in the database there's already the Constraint, 
> >>     # requested, if yes return that object, if not create a new one. 
> >>     # This is an adaptation of the UniqueObject pattern 
> >>     # suggested by SQLAlchemy documentation 
> >>     # 
> https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/UniqueObject 
> >>     if isinstance(instance, UniqueAssociationProxy): 
> >>         instance = instance.ensure_unicity(session) 
> >> 
> >> 
> >> 
> >> 
> >> On Thursday, January 4, 2018 at 6:05:38 PM UTC+1, Ruben Di Battista 
> wrote: 
> >>> 
> >>> Hello, 
> >>> I'm writing a satellite passage scheduler that has a database 
> persistence 
> >>> layer to store the scheduled passages. 
> >>> 
> >>> The DB schema is organized as follows: 
> >>> - A table storing the satellites (using NORAD No as Primary Key) 
> >>> - A table storing the ground stations where to compute the passages of 
> the 
> >>> satellites 
> >>> - A table storing the passages of these satellites, with two foreign 
> keys 
> >>> linking each passage to a Ground Station and a Satellite 
> >>> - A table storing all the types of constraints a satellite can have 
> >>> - A table storing all the types of weights (used to perform the 
> >>> scheduling) that can be assigned to each satellite 
> >>> 
> >>> Than I configured some association proxies (with the related `Unique 
> >>> Object` pattern) in order to assign the weights and the satellites as 
> a 
> >>> dictionary 
> >>> 
> >>> sat.constraints['min_elevation']= 10 
> >>> 
> >>> The details of the relationships here below: 
> >>> 
> >>> # Relationship definitions 
> >>> orm.mapper(Satellite, satellite, properties={ 
> >>>     'passages': orm.relationship(Passage, 
> >>>                                  backref='satellite', 
> >>>                                  order_by=passage.c.aos, 
> >>>                                  cascade='all, delete-orphan'), 
> >>> 
> >>> 
> >>>     '_constraints': orm.relationship( 
> >>>         ConstrainedSatellite, backref='satellite', 
> >>> 
> >>> collection_class=orm.collections.attribute_mapped_collection('name'), 
> >>>         cascade='all, delete-orphan'), 
> >>> 
> >>> 
> >>>     '_weights': orm.relationship( 
> >>>         WeightedSatellite, backref='satellite', 
> >>> 
> >>> collection_class=orm.collections.attribute_mapped_collection('name'), 
> >>>         lazy='joined', 
> >>>         cascade='all, delete-orphan'), 
> >>> 
> >>> 
> >>>     '_tle': satellite.c.tle 
> >>> 
> >>> 
> >>> }) 
> >>> 
> >>> 
> >>> orm.mapper(Constraint, constraint, properties={ 
> >>>     'satellites': orm.relationship(ConstrainedSatellite, 
> >>> backref='constraint') 
> >>> 
> >>> 
> >>> }) 
> >>> 
> >>> 
> >>> orm.mapper(Weight, weight, properties={ 
> >>>     'satellites': orm.relationship(WeightedSatellite, 
> backref='weight') 
> >>> }) 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> orm.mapper(ConstrainedSatellite, constraint_satellite) 
> >>> 
> >>> 
> >>> orm.mapper(WeightedSatellite, weight_satellite) 
> >>> 
> >>> 
> >>> orm.mapper(PassageData, passage_data) 
> >>> 
> >>> 
> >>> orm.mapper(Passage, passage, properties={ 
> >>>     'angles': orm.relationship(PassageData, backref='passage', 
> >>>                                order_by=passage_data.c.time, 
> >>>                                cascade='all, delete-orphan') 
> >>> 
> >>> 
> >>> }, 
> >>>     confirm_deleted_rows=False 
> >>> ) 
> >>> 
> >>> 
> >>> orm.mapper(GroundStation, ground_station, properties={ 
> >>>     'passages': orm.relationship(Passage, backref='ground_station', 
> >>>                                  order_by=passage.c.aos, 
> >>>                                  cascade='all, delete-orphan') 
> >>> }) 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> # Association Proxies 
> >>> Satellite.constraints = association_proxy( 
> >>>     '_constraints', 'value', creator=constrained_sat_creator 
> >>> ) 
> >>> 
> >>> 
> >>> Satellite.weights = association_proxy( 
> >>>     '_weights', 'value', creator=weighted_sat_creator 
> >>> ) 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> ConstrainedSatellite.constraint_name = association_proxy('constraint', 
> >>> 'name') 
> >>> WeightedSatellite.weight_name = association_proxy('weight', 'name') 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> From the tests everything is working as expected. The problem is that 
> I'm 
> >>> getting some performance issues while performing the scheduling 
> procedure: 
> >>> this procedure needs to read the `constraints` and `weights` 
> associated to 
> >>> each satellite to select the best passages among all the ones possible 
> on 
> >>> each ground station. While reading the values of the `constraints` and 
> >>> `weights`, SQLAlchemy needs to store the Passage object in the 
> Database. So 
> >>> all the passages are stored in the database during the propagation of 
> all 
> >>> the possible passages, and then I need to manually expunge or delete 
> the 
> >>> passages that haven't been scheduled from the DB. 
> >>> 
> >>> What I would like to achieve is to "shut down", after selecting the 
> ground 
> >>> stations and the satellites for which to perform the optimization, the 
> >>> SQLAlchemy persistence in order to perform the optimization procedure 
> only 
> >>> on Python objects, without having SQLAlchemy to store them in the DB, 
> and 
> >>> then just storing efficiently the optimized, smaller, list of them 
> that are 
> >>> computed by the scheduling algorithm. 
> >>> 
> >>> Currently the scheduling script is like this (schematically): 
> >>> 
> >>> 
> >>>     # Create a DB session 
> >>>     session_factory = orm.sessionmaker(db) 
> >>>     session = orm.scoped_session(session_factory) 
> >>> 
> >>> 
> >>>     # Retrieve satellites from DB 
> >>>     sats = session.query(Satellite).all() 
> >>> 
> >>> 
> >>>     # Retrieve gss 
> >>>     ground_stations = session.query(GroundStation).all() 
> >>> 
> >>> 
> >>>     # Init Scheduler instance 
> >>>     scheduler = Scheduler(sats, ground_stations, start_day) 
> >>> 
> >>> 
> >>>     # This methods generates all the possible passages of all 
> satellites 
> >>> on all ground stations 
> >>>     # it needs to read `constraints` and `weights` for each satellite 
> to 
> >>> perform what it needs. 
> >>>     # currently all the passages get stored in the DB 
> >>>     all_passages = scheduler.propagate() 
> >>> 
> >>> 
> >>>     # This method selects from the totality of all_passages, a subset 
> that 
> >>> fulfills all 
> >>>     # the constraints. 
> >>>     scheduled_passages = prop.schedule(all_passages, iterations, pool) 
> >>> 
> >>> 
> >>>     # Remove from session passages not scheduled 
> >>>     # === This is the thing I would like to avoid since it's slow === 
> >>>     for passage in all_passages: 
> >>>         if passage not in scheduler_passages: 
> >>>             session.expunge(passage) 
> >>>             session.delete(passage) 
> >>> 
> >>> 
> >>>     session.commit() 
> >>>     session.close() 
> >>> 
> >>> As you can see, I need to manually expunge or delete the passages that 
> are 
> >>> not scheduled by the algorithm. If the number of passages is huge, 
> that 
> >>> means performing a DELETE query for each passage, that is slow. What I 
> would 
> >>> like to achieve is to have `scheduler.propagate`, and 
> `scheduler.schedule` 
> >>> method to perform operations only on Python objects (fully loaded with 
> all 
> >>> the correct parameters from the DB), avoiding the need to store and 
> then 
> >>> delete all the passages in the DB. Can I "shut down" temporarily the 
> DB 
> >>> persistence? 
> >>> 
> >>> Thanks for you help, 
> >>> RdB 
> >>> 
> >>> PS: I'm on MySQL (MariaDB) 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> 
> >>> 
> >> -- 
> >> 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+...@googlegroups.com <javascript:>. 
> >> To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:>. 
> >> 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+...@googlegroups.com <javascript:>. 
> > To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:>. 
> > 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.

Reply via email to