On Fri, Sep 28, 2018 at 9:41 PM seaders <seader...@gmail.com> wrote: > > I'm writing some tests for my system, and I have all the objects that I > *would* be getting from the database, as flat json files, which I then load > up from, to SQLAlchemy files for my tests. > > I've all that working fine, but the one place I'd like to improve is "gluing" > them back together / creating the mapping associations. > > Consider the following, > > class Sport(Base): > id = Column(Integer, primary_key=True) > name = Column(String(50), unique=True) > > class Competition(Base): > id = Column(Integer, primary_key=True) > name = Column(String(50), unique=True) > > sport_id = Column(Integer, ForeignKey(Sport.id)) > sport = relationship(Sport) > > > If I run > > sport = Sport.query.filter(Sport.id == 1).first() > comp = Competition.query.filter(Competition.sport == sport) > assert sport == comp.sport > > > Everything's good. > > However, if I create a Competition as a "flat" object, > > comp2 = Competition(id=2345, name='My Comp', sport_id=1) > session.add(comp2) > assert sport == comp2.sport > > > This one fails, because sport on comp2 is null. > > Poking through the code, I see it's mainly / probably to do with the logic > for _load_for_state in strategies, > > def _load_for_state(self, state, passive): > > if not state.key and ( > ( > not self.parent_property.load_on_pending > and not state._load_pending > ) > or not state.session_id > ): > return attributes.ATTR_EMPTY > > > comp has a state.key for it's sport column, whereas comp2 doesn't (and both > self.parent_property.load_on_pending and state._load_pending are false. > > So yeah, that's my overall question. With an object that's *not* gone into, > nor come back from the database, is there some global option, or something > along those lines so that when I check properties of those objects, the > mapper does its thing, and maps to its objects, and not ATTR_EMPTY / None?
yes that's what those two flags are doing. the way the ORM is designed, it wants you to be setting comp.sport = sport, not comp.sport_id = id, but there's a fair degree of special features these days that allow people to work the other way since it is so commonly requested, which are specifically what those two flags are for. an overview of the situation is at https://docs.sqlalchemy.org/en/latest/faq/sessions.html#i-set-the-foo-id-attribute-on-my-instance-to-7-but-the-foo-attribute-is-still-none-shouldn-t-it-have-loaded-foo-with-id-7, which is probably the first FAQ question we've ever had. The API features that correspond to load_on_pending and load_pending are the load_on_pending relationship flag: https://docs.sqlalchemy.org/en/latest/orm/relationship_api.html?highlight=load_on_pending#sqlalchemy.orm.relationship.params.load_on_pending , and enable_relationship_loading at https://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.enable_relationship_loading, which does the same thing with a different scope, that is, for individual objects rather than an entire mapping. both APIs do a similar thing, it's just that load_on_pending is set at the level of the class mapping on the relationship, whereas enable_relationship_loading you can just enable for any individual object without any change to the mapping which may be better for running tests. since I never use these I feel I had to test them out to make sure I'm not mis-characterizing, so it looks like this. here is load_on_pending: class A(Base): __tablename__ = 'a' id = Column(Integer, primary_key=True) bs = relationship("B", backref=backref("a", load_on_pending=True)) class B(Base): __tablename__ = 'b' id = Column(Integer, primary_key=True) a_id = Column(ForeignKey("a.id")) s = Session(e) a1 = A(id=1) s.add(a1) s.commit() b1 = B(a_id=1) s.add(b1) # note you have to add b1 to the session for it to be "pending" assert b1.a is a1 Here is enable_relationship_loading: class A(Base): __tablename__ = 'a' id = Column(Integer, primary_key=True) bs = relationship("B", backref="a") class B(Base): __tablename__ = 'b' id = Column(Integer, primary_key=True) a_id = Column(ForeignKey("a.id")) a1 = A(id=1) s.add(a1) s.commit() b1 = B(a_id=1) s.enable_relationship_loading(b1) # note you *don't* have to add to the session for this assert b1.a is a1 you can also add b1 to the session at any time above. hope this helps. > > > -- > 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.