the next 1.3 release will include a warning for this case, whether or
not the primary key on the transient object is set up or not; it does
a double insert in any case otherwise.   It's just when the primary
key is already present, the double insert fails on the primary key
constraint.

https://github.com/sqlalchemy/sqlalchemy/issues/4647


On Sun, Apr 28, 2019 at 12:26 PM Mike Bayer <mike...@zzzcomputing.com> wrote:
>
> Hi there,
>
> I appreciate everyone chiming in to look at this!
>
> However, it's something simple and has to do with one of the API
> quirks that we have to decide if we want to keep long term.
> Background is at
> https://docs.sqlalchemy.org/en/13/orm/cascades.html#backref-cascade.
>
> Basically, when you do this:
>
>     my_head = Head(id="foobar")
>     my_head = db_session.merge(my_head)
>     db_session.commit()
>
>     my_tassel_thread = TasselThread(head=my_head)
>
> "my_tassel_thread" is now in the Session due to the backref cascade,
> which I think I really might consider defaulting to False at some
> point.   it also has no primary key value yet.  So when you merge()
> it, it gets put in a second time, again with no primary key.
>
> Another thing that makes this really bad on my part, is that if you
> flush the session before the merge, then my_tassel_thread gets a new
> primary key, then the merge is of itself and it works.   This is bad
> because it suggests merge() should be calling flush() automatically,
> but im not sure that's a good idea in the bigger scheme of things.
>
> Short answer, set up the relationships like:
>
>     tassel_threads = relationship("TasselThread",
> back_populates="head", cascade_backrefs=False)
>
>     # ...
>
>     head = relationship("Head", back_populates="tassel_threads",
> cascade_backrefs=False)
>
>
> and then my_tassel_thread stays out of the Session.
>
> Also:
>
> > db_engine = create_engine('sqlite:///sample.db', convert_unicode=True)
>
> don't use convert_unicode, it's deprecated, has no purpose in the
> modern Python ecosystem, and is going away.     SQLite in particular
> is a fully Python unicode backend that's impossible to get a plain
> string out of.
>
>
>
>
> On Sun, Apr 28, 2019 at 8:56 AM <lylafi...@gmail.com> wrote:
> >
> > Hi!
> >
> > I recently came across some confusing behavior in relations and cascading 
> > using sqllite, and I was hoping that I might get some help explaining what 
> > the behavior is here. I put together a minimum failing script here. I'm 
> > trying to commit one instance of each of two classes, but what ends up 
> > happening is that I commit two copies of the many part of a one-to-many 
> > relation. I suspect that this has something to do with cascading, but I 
> > found a bug report for similar behavior that claims to have been fixed 
> > several years ago, and I'm wondering if there was some kind of regression? 
> > I'm running SQLAlchemy 1.3.1 on Ubuntu and I'm still using sqllite at this 
> > stage of development.
> >
> > from sqlalchemy import create_engine
> > from sqlalchemy.orm import scoped_session, sessionmaker
> > from sqlalchemy.ext.declarative import declarative_base
> > from sqlalchemy import Column, Integer, String, Text, ForeignKey
> > from sqlalchemy.orm import relationship
> >
> > import os
> >
> > db_engine = create_engine('sqlite:///sample.db', convert_unicode=True)
> > db_session = scoped_session(sessionmaker(autocommit=False,
> >                                          autoflush=False,
> >                                          bind=db_engine))
> >
> > Base = declarative_base()
> > Base.query = db_session.query_property()
> >
> > class Head(Base):
> >     __tablename__ = 'head'
> >     id = Column(String, primary_key=True)
> >     tassel_threads = relationship("TasselThread", back_populates="head")
> >     def __init__(self, id):
> >         self.id=id
> >
> > class TasselThread(Base):
> >     __tablename__ = 'tassel_thread'
> >     id = Column(Integer, primary_key=True)
> >     head_id = Column(Integer, ForeignKey('head.id'), nullable=False)
> >     head = relationship("Head", back_populates="tassel_threads")
> >     def __init__(self, head):
> >         self.head = head
> >
> > def init_db():
> >     Base.metadata.create_all(bind=db_engine)
> >
> >
> > def do_db_work():
> >
> >     my_head = Head(id="foobar")
> >     my_head = db_session.merge(my_head)
> >     db_session.commit()
> >
> >     my_tassel_thread = TasselThread(head=my_head)
> >     db_session.merge(my_tassel_thread)
> >     db_session.commit()
> >
> >
> > if os.path.exists("sample_data.db"):
> >     os.remove("sample_data.db")
> > init_db()
> > do_db_work()
> > a = db_session.query(TasselThread).all()
> > print(len(a))
> > # output: 2, should be 1
> >
> > Thanks for any help you might be able to provide!
> >
> > -Lyla Fischer
> >
> > --
> > 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.

Reply via email to