[sqlalchemy] Regarding the use of reciprocal relationships
Related to one of my recent posted threads here, I'm recalling a certain conversation at PyCon where I was mentioning how a friend would define a many-to-many relationship by defining a relationship on both declarative classes involved, each pointing to the other, and the look of abject horror I received gave a good indication that this is very often not a good thing. However, I've run into a situation where this has proven useful: class Deployment(Base): __tablename__ = 'deployments' id = Column(u'DeploymentID', INTEGER(), primary_key=True) package_id = Column( INTEGER(), ForeignKey('packages.package_id', ondelete='cascade'), nullable=False ) package = relationship('Package') class Package(Base): __tablename__ = 'packages' id = Column(u'package_id', INTEGER(), primary_key=True) pkg_name = Column(String(length=255), nullable=False) version = Column(String(length=63), nullable=False) revision = Column(String(length=63), nullable=False) deployments = relationship('Deployment') In this case, most of the time I'm looking to determine which deployments a given package belongs to, but there are times when I have a given deployment and am curious as to what package(s) are related to it, and unless I'm misunderstanding something (which I won't rule out I could be), a backref won't easily help in this instance. Of course, one of the gotchas in using this (and we did hit it in one of our queries) is if not careful, one can trigger a nice 'maximum recursion depth exceeded' exception (in our particular case, a 'joinedload' within the query was able to resolve that issue) and I'm sure there other poten- tial gotchas, so I guess this leads back to the main question at hand: Are there times when using reciprocal relationships is okay, and are there certain things that should be done to mitigate potential issues that can be caused by doing so, or are there better ways to accomplish the same thing? -- - Ken Lareau -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
Re: [sqlalchemy] Regarding the use of reciprocal relationships
On 7/1/14, 4:54 PM, Ken Lareau wrote: Related to one of my recent posted threads here, I'm recalling a certain conversation at PyCon where I was mentioning how a friend would define a many-to-many relationship by defining a relationship on both declarative classes involved, each pointing to the other, and the look of abject horror I received gave a good indication that this is very often not a good thing. However, I've run into a situation where this has proven useful: class Deployment(Base): __tablename__ = 'deployments' id = Column(u'DeploymentID', INTEGER(), primary_key=True) package_id = Column( INTEGER(), ForeignKey('packages.package_id', ondelete='cascade'), nullable=False ) package = relationship('Package') class Package(Base): __tablename__ = 'packages' id = Column(u'package_id', INTEGER(), primary_key=True) pkg_name = Column(String(length=255), nullable=False) version = Column(String(length=63), nullable=False) revision = Column(String(length=63), nullable=False) deployments = relationship('Deployment') In this case, most of the time I'm looking to determine which deployments a given package belongs to, but there are times when I have a given deployment and am curious as to what package(s) are related to it, and unless I'm misunderstanding something (which I won't rule out I could be), a backref won't easily help in this instance. I'm not actually familiar with what you mean by a backref won't easily help. Above, Package.deployments and Deployment.package refer to the same linkage between the two classes, just in different directions. A backref above is equivalent to what you have, plus: class Deployment(Base): # ... package = relationship(Package, back_populates='deployments') class Package(Base): # ... deployments = relationship(Deployment, back_populates='package') the importance of back_populates (or backref, essentially a shortcut to the same thing) is mostly in how the collection or attribute responds to in-Python changes and to a lesser (but sometimes important) extent how the attributes are treated during the flush process. The two relationships don't have to be related to each other at the Python level like this, but there's usually no reason to omit this information as it only allows the ORM to better keep the two sides in sync. Of course, one of the gotchas in using this (and we did hit it in one of our queries) is if not careful, one can trigger a nice 'maximum recursion depth exceeded' exception (in our particular case, a 'joinedload' within the query was able to resolve that issue) and I'm sure there other poten- tial gotchas, so I guess this leads back to the main question at hand: I don't doubt that this can happen but I'm not familiar at the moment what the nature of this recursion issue would be. Are there times when using reciprocal relationships is okay, and are there certain things that should be done to mitigate potential issues that can be caused by doing so, or are there better ways to accomplish the same thing? It's not clear to me what the thing is that you want to accomplish here. If it's just, you want to set up the two relationships as explicit code for readability, that's great, use back_populates. This is probably how apps should be doing it anyway, in the early SQLAlchemy days there was a lot of pressure to not require too much boilerplate, hence backref. These days, the community has moved well past the whole notion of super minimal declaration / magic == good, thankfully. -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
Re: [sqlalchemy] Regarding the use of reciprocal relationships
On Tue, Jul 1, 2014 at 2:17 PM, Mike Bayer mike...@zzzcomputing.com wrote: On 7/1/14, 4:54 PM, Ken Lareau wrote: Related to one of my recent posted threads here, I'm recalling a certain conversation at PyCon where I was mentioning how a friend would define a many-to-many relationship by defining a relationship on both declarative classes involved, each pointing to the other, and the look of abject horror I received gave a good indication that this is very often not a good thing. However, I've run into a situation where this has proven useful: class Deployment(Base): __tablename__ = 'deployments' id = Column(u'DeploymentID', INTEGER(), primary_key=True) package_id = Column( INTEGER(), ForeignKey('packages.package_id', ondelete='cascade'), nullable=False ) package = relationship('Package') class Package(Base): __tablename__ = 'packages' id = Column(u'package_id', INTEGER(), primary_key=True) pkg_name = Column(String(length=255), nullable=False) version = Column(String(length=63), nullable=False) revision = Column(String(length=63), nullable=False) deployments = relationship('Deployment') In this case, most of the time I'm looking to determine which deployments a given package belongs to, but there are times when I have a given deployment and am curious as to what package(s) are related to it, and unless I'm misunderstanding something (which I won't rule out I could be), a backref won't easily help in this instance. I'm not actually familiar with what you mean by a backref won't easily help. Above, Package.deployments and Deployment.package refer to the same linkage between the two classes, just in different directions. A backref above is equivalent to what you have, plus: class Deployment(Base): # ... package = relationship(Package, back_populates='deployments') class Package(Base): # ... deployments = relationship(Deployment, back_populates='package') the importance of back_populates (or backref, essentially a shortcut to the same thing) is mostly in how the collection or attribute responds to in-Python changes and to a lesser (but sometimes important) extent how the attributes are treated during the flush process. The two relationships don't have to be related to each other at the Python level like this, but there's usually no reason to omit this information as it only allows the ORM to better keep the two sides in sync. Oy, leave it to me to misinterpret once again (and I have read that part of the docs before, so I should have known better). For some reason I had in my head that the backref could only be used from the class that the relationship was defined on. :-/ Of course, one of the gotchas in using this (and we did hit it in one of our queries) is if not careful, one can trigger a nice 'maximum recursion depth exceeded' exception (in our particular case, a 'joinedload' within the query was able to resolve that issue) and I'm sure there other poten- tial gotchas, so I guess this leads back to the main question at hand: I don't doubt that this can happen but I'm not familiar at the moment what the nature of this recursion issue would be. Talking with my coworker again, I believe I may have misspoke on the 'joinedload' solving the issue (we used that for performance improvements I think), but it was while we were trying things out that we caused the recursion issue and sadly neither of us can remember exactly what trig- gered that! So please forget that part, my memory is terrible as of late. *smile* Are there times when using reciprocal relationships is okay, and are there certain things that should be done to mitigate potential issues that can be caused by doing so, or are there better ways to accomplish the same thing? It's not clear to me what the thing is that you want to accomplish here. If it's just, you want to set up the two relationships as explicit code for readability, that's great, use back_populates. This is probably how apps should be doing it anyway, in the early SQLAlchemy days there was a lot of pressure to not require too much boilerplate, hence backref. These days, the community has moved well past the whole notion of super minimal declaration / magic == good, thankfully. We've gone ahead and utilized the 'back_populates' for this case, and I thank you for the assistance and clarification here! -- - Ken Lareau -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
Re: [sqlalchemy] Regarding the use of reciprocal relationships
If it's just, you want to set up the two relationships as explicit code for readability, that's great, use back_populates. This is probably how apps should be doing it anyway, in the early SQLAlchemy days there was a lot of pressure to not require too much boilerplate, hence backref. These days, the community has moved well past the whole notion of super minimal declaration / magic == good, thankfully. Personally, I would LOVE a strict config setting that would raise an exception if both sides of a relationship weren't explicitly defined. This is convenient... class Users(Base): addresses = relationship(Addresses, backref=users) class Addresses(Base): pass But when you're tracking down bugs and dealing with code that probably (honestly) should have failed a peer review, that can make all the difference. -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.