Description
Hey, I have three models:
class Prelegent(Base, TimeStampedModel):
__tablename__ = 'prelegents'
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(64), nullable=False)
last_name = Column(Unicode(64), nullable=False)
events = relationship(
"Event", backref=backref("prelegents", lazy='dynamic'),
secondary="prelegents_events")
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True, autoincrement=True)
class PrelegentEvent(Base):
__tablename__ = 'prelegents_events'
event_id = Column(
Integer, ForeignKey('events.id', ondelete='cascade'),
index=True, nullable=False, primary_key=True)
prelegent_id = Column(
Integer, ForeignKey('prelegents.id', ondelete='cascade'),
index=True, nullable=False, primary_key=True)
event = relationship("Event", backref="prelegents_event")
prelegent = relationship("Prelegent", lazy="joined")
and in sqlalchemy 1.3+ all was working without any working, now after switching to 1.4+ I am getting warnings after my test case run:
/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'Event.prelegents_event' will copy column events.id to column prelegents_events.event_id, which conflicts with relationship(s): 'Event.prelegents' (copies events.id to prelegents_events.event_id), 'Prelegent.events' (copies events.id to prelegents_events.event_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'PrelegentEvent.event' will copy column events.id to column prelegents_events.event_id, which conflicts with relationship(s): 'Event.prelegents' (copies events.id to prelegents_events.event_id), 'Prelegent.events' (copies events.id to prelegents_events.event_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'PrelegentEvent.prelegent' will copy column prelegents.id to column prelegents_events.prelegent_id, which conflicts with relationship(s): 'Event.prelegents' (copies prelegents.id to prelegents_events.prelegent_id), 'Prelegent.events' (copies prelegents.id to prelegents_events.prelegent_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
URL inside warning suggests that I am not using proper back_populates. In the example there, there is no backref or backpopulates but I am having backref here and still getting this warning. Also in the documentation, you can read that:
Remember, when the relationship.backref keyword is used on a single relationship, it’s exactly the same as if the above two relationships were created individually using relationship.back_populates on each.
Anyway I tried and switch my version to use back_populates:
class Prelegent(Base, TimeStampedModel):
__tablename__ = 'prelegents'
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(64), nullable=False)
last_name = Column(Unicode(64), nullable=False)
events = relationship(
"Event", backref=backref("prelegents", lazy='dynamic'),
secondary="prelegents_events")
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True, autoincrement=True)
prelegents_event = relationship("PrelegentEvent", back_populates="event")
class PrelegentEvent(Base):
__tablename__ = 'prelegents_events'
event_id = Column(
Integer, ForeignKey('events.id', ondelete='cascade'),
index=True, nullable=False, primary_key=True)
prelegent_id = Column(
Integer, ForeignKey('prelegents.id', ondelete='cascade'),
index=True, nullable=False, primary_key=True)
event = relationship("Event", back_populates="prelegents_event")
prelegent = relationship("Prelegent", lazy="joined")
After that, I am getting only 2 warnings instead of 3.
/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'PrelegentEvent.event' will copy column events.id to column prelegents_events.event_id, which conflicts with relationship(s): 'Event.prelegents' (copies events.id to prelegents_events.event_id), 'Prelegent.events' (copies events.id to prelegents_events.event_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'PrelegentEvent.prelegent' will copy column prelegents.id to column prelegents_events.prelegent_id, which conflicts with relationship(s): 'Event.prelegents' (copies prelegents.id to prelegents_events.prelegent_id), 'Prelegent.events' (copies prelegents.id to prelegents_events.prelegent_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
I think this is because of the Prelegent model but I don't know how to implement this correctly here:
events = relationship(
"Event", backref=backref("prelegents", lazy='dynamic'),
secondary="prelegents_events")
Viewonly will not work here because I am using these columns also in my application.
Could you help me with this?
Versions
- OS: Ubuntu 20.04
- Python: 3.8.0
- SQLAlchemy: 1.4.11
- Database: postgres
Activity
zzzeek commentedon Apr 29, 2021
from a quick look it seeems like you should be placing viewonly on:
events = relationship(
"Event", backref=backref("prelegents", lazy='dynamic', viewonly=True),
secondary="prelegents_events", viewonly=True)
viewonly doesn't imply that you can't "use" a column, above it just means you shouldn't mutate the Prelegent.events or Event.prelegents collections. If you want to add new rows in the "prelegent_events" table, you would construct and add new PrelegentEvent objects.
that's why I'm leaving this here, as we have made some adjustments in this area in #5171 we want to make sure it's not doing anything wrong. It's a little bit of a surprise that 1.3 wasn't warning for this but it looks like these checks were very narrow in 1.3.
[-]solution to relationship X will copy column Q to column P, which conflicts with relationship(s): ‘Y’[/-][+]warn_on_conflicting_targets seems to work for "secondary" conflicts now but we have no tests for this?[/+]Rogalek commentedon Apr 29, 2021
so, when I change viewonly to True, this will not work - that was what I meant by saying that I am using these columns:
so I have to change it delete each object and then create a new one, yes? Just double-checking if I understood correctly. Thank you for your help.
Yeah, in 1.3 there were no warnings about that and now most of my models are returning this kind of warnings.
zzzeek commentedon Apr 29, 2021
well you can use one or the other, if you put viewonly on the "event" and "prelagent_event" relationships, that would work too.
the warnings are trying to prevent you from making conflicting changes in the "prelegents_events" table, since you have essentially mapped to this table twice in two different ways. SQLAlchemy wants you to write rows into this table using only one of those ways. technically, even if you set viewonly on the PrelegentEvent relationships, you can still create conflicting rows in these two tables, but in this case there are actual relationships overlapping on these columns.
if you really want to leave everything the same and not have warnings then you can use the overlaps parameter, which should be mentioned in the linked doc. overlaps means, "yes it's fine, two ways to write to this column"
Rogalek commentedon Apr 30, 2021
Thanks, I used
viewonly=True
and changed code a bit and everything works well in version 1.4.11, no more warning.Rogalek commentedon May 10, 2021
just for the purpose of knowing could you please show me how to change this:
to
overlaps
?zzzeek commentedon May 10, 2021
keeping in mind the "overlaps" case wasn't originally something I had noted for this case, and that it would be nice to reduce the verbosity here somehow, for the moment every relationship or backref() has to name every other relationship it conflicts with, so like this:
Rogalek commentedon May 11, 2021
hey, I wanted to use this also on my other models but I am struggling with one relationship.
I have models like this:
but I am still getting:
what am I doing wrong? Thanks.
zzzeek commentedon May 11, 2021
might be a good idea to make the warning spell this out but you can get the overlaps instruction from each warning:
/python_modules/sqlalchemy/lib/sqlalchemy/orm/relationships.py:3441: SAWarning: relationship 'ExhibitorCategoryAssociation.exhibitor' will copy column exhibitors.id to column exhibitors_x_exhibitor_categories.exhibitor_id, which conflicts with relationship(s): 'Exhibitor.categories' (copies exhibitors.id to exhibitors_x_exhibitor_categories.exhibitor_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning. (Background on this error at: http://sqlalche.me/e/14/qzyx)
you would go to the ExhibitorCategoryAssociation.exhibitor relationship, and set overlaps="categories".
that is. pull the instructions straight from each warning message.
Rogalek commentedon May 12, 2021
Ok, thanks for the explanation.
sqla-tester commentedon Jun 1, 2021
Mike Bayer has proposed a fix for this issue in the master branch:
improve "overlaps" warning; test for m2m https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/2851