Re: [sqlalchemy] Invalidated Collection

2021-02-04 Thread Christian Henning
Thanks, Mike! I have some studying to do...

On Wednesday, February 3, 2021 at 6:42:17 PM UTC-5 Mike Bayer wrote:

>
>
> On Wed, Feb 3, 2021, at 6:23 PM, Christian Henning wrote:
>
> Hi Mike,
>
> thanks for your advice! I'll make the changes.
>
> But let me ask you one thing. My classmethod create() is but more complex 
> than I have posted. It's meant to catch IntegrityError so that unique 
> constraints are enforced. Image a User table has a "unique" constraint on 
> the name. When I try to create a user with the same name the create() would 
> catch the exception and return the original user. I believe this only 
> possible when all objects are committed to the database.
>
> class ModelBase:
>
>   @classmethod
>   def create(cls, session, **kwargs):
> obj = cls(**kwargs)
> try:
>   obj.save(session)
> except IntegrityError:
>   # print('HOLA - Integrity Error')
>
>   session.rollback()
>   obj = cls.find(session, **kwargs)
>
> return obj
>
>   def save(self, session):
> session.add(self)
> session.commit()
>
> How would you solve the problem? Is there anything in the documentation?
>
>
> yes, use a SAVEPOINT for that, so you can roll back to the savepoint and 
> still have the transaction continue:
>
>
> https://docs.sqlalchemy.org/en/13/faq/sessions.html#but-why-does-flush-insist-on-issuing-a-rollback
>
> so you can place in your method:
>
> session.begin_nested():
> session.add(obj)
> try:
>  session.flush()
>  except IntegrityError:
> session.rollback()
> else:
> session.commit()
>
>
>
>
> Thanks,
> Christian
>
>
>
>
>
> On Wednesday, February 3, 2021 at 5:12:30 PM UTC-5 Mike Bayer wrote:
>
>
> the session.commit() method expires all attributes by default:
>
> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing
>
> your code is organized in an unusual way such that transactions are being 
> committed inside of attribute assignment operations:
>
> jack.addresses.append(Address.create(session))
>
> We can expand this out as follows:
>
> addr = jack.addresses
> address = Address()
> session.add(address)
> session.commit()   # <--- expires all attributes on "jack", "address", 
> invalidates "addr"
> attr.append(address)  # <--- this is invalid, transaction was already 
> commited
>
> I would strongly advise keeping session scope manage external to the set 
> of operations you are performing.  See 
> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it
>  
> for guidelines.
>
>
> On Wed, Feb 3, 2021, at 2:27 PM, Christian Henning wrote:
>
> I don't understand why SQLAlchemy gives me the following warning:
>
> SAWarning: This collection has been invalidated.
>   util.warn("This collection has been invalidated.")
>
> Here is a minimal code example:
>
> -
> -
>
> import sqlalchemy
> print(sqlalchemy.__version__)
>
> from sqlalchemy import create_engine
> from sqlalchemy import Column, Integer, ForeignKey
>
> from sqlalchemy.exc import IntegrityError
> from sqlalchemy.ext.declarative import declarative_base
>
> from sqlalchemy.orm import relationship
> from sqlalchemy.orm import sessionmaker
>
> Base = declarative_base()
>
> class ModelBase:
>
>   @classmethod
>   def create(cls, session, **kwargs):
> obj = cls(**kwargs)
> session.add(obj)
> session.commit()
>
> return obj
>
>
> class User(ModelBase, Base):
>   __tablename__ = 'users'
>
>   id = Column(Integer, primary_key=True)
>
>   addresses = relationship("Address", order_by="Address.id", 
> back_populates="user")
>
> class Address(ModelBase, Base):
>   __tablename__ = 'addresses'
>
>   id = Column(Integer, primary_key=True)
>
>   user_id = Column(Integer, ForeignKey('users.id'))
>
>   user = relationship("User", back_populates="addresses")
>
>
> engine = create_engine('sqlite:///:memory:', echo=False)
> Base.metadata.create_all(engine)
>
> Session = sessionmaker(bind=engine)
> session = Session()
>
> jack = User.create(session)
> jack.addresses.append(Address.create(session))
>
> -
> -
> I have tested the code with SQLAlchemy for 1.3.18 and 1.4.0b1. I'm using 
> python 3.9.1
>
> When I rewrite the last few lines a bit the warning disappears. For 
> instance:
>
> jack = User.create(session)
> a = Address.create(session)
> jack.addresses.append(a)
>
>
> Any insight is appreciated. I'm still learning.
>
> Thanks,
> Christian
>
>
>
> -- 
> 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 

Re: [sqlalchemy] Invalidated Collection

2021-02-03 Thread Mike Bayer


On Wed, Feb 3, 2021, at 6:23 PM, Christian Henning wrote:
> Hi Mike,
> 
> thanks for your advice! I'll make the changes.
> 
> But let me ask you one thing. My classmethod create() is but more complex 
> than I have posted. It's meant to catch IntegrityError so that unique 
> constraints are enforced. Image a User table has a "unique" constraint on the 
> name. When I try to create a user with the same name the create() would catch 
> the exception and return the original user. I believe this only possible when 
> all objects are committed to the database.
> 
> class ModelBase:
> 
>   @classmethod
>   def create(cls, session, **kwargs):
> obj = cls(**kwargs)
> try:
>   obj.save(session)
> except IntegrityError:
>   # print('HOLA - Integrity Error')
> 
>   session.rollback()
>   obj = cls.find(session, **kwargs)
> 
> return obj
> 
>   def save(self, session):
> session.add(self)
> session.commit()
> 
> How would you solve the problem? Is there anything in the documentation?

yes, use a SAVEPOINT for that, so you can roll back to the savepoint and still 
have the transaction continue:

https://docs.sqlalchemy.org/en/13/faq/sessions.html#but-why-does-flush-insist-on-issuing-a-rollback

so you can place in your method:

session.begin_nested():
session.add(obj)
try:
 session.flush()
 except IntegrityError:
session.rollback()
else:
session.commit()



> 
> Thanks,
> Christian
> 
> 
> 
> 
> 
> On Wednesday, February 3, 2021 at 5:12:30 PM UTC-5 Mike Bayer wrote:
>> __
>> the session.commit() method expires all attributes by default:
>> 
>> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing
>> 
>> your code is organized in an unusual way such that transactions are being 
>> committed inside of attribute assignment operations:
>> 
>> jack.addresses.append(Address.create(session))
>> 
>> We can expand this out as follows:
>> 
>> addr = jack.addresses
>> address = Address()
>> session.add(address)
>> session.commit()   # <--- expires all attributes on "jack", "address", 
>> invalidates "addr"
>> attr.append(address)  # <--- this is invalid, transaction was already 
>> commited
>> 
>> I would strongly advise keeping session scope manage external to the set of 
>> operations you are performing.  See 
>> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it
>>  for guidelines.
>> 
>> 
>> On Wed, Feb 3, 2021, at 2:27 PM, Christian Henning wrote:
>>> I don't understand why SQLAlchemy gives me the following warning:
>>> 
>>> SAWarning: This collection has been invalidated.
>>>   util.warn("This collection has been invalidated.")
>>> 
>>> Here is a minimal code example:
>>> 
>>> -
>>> -
>>> 
>>> import sqlalchemy
>>> print(sqlalchemy.__version__)
>>> 
>>> from sqlalchemy import create_engine
>>> from sqlalchemy import Column, Integer, ForeignKey
>>> 
>>> from sqlalchemy.exc import IntegrityError
>>> from sqlalchemy.ext.declarative import declarative_base
>>> 
>>> from sqlalchemy.orm import relationship
>>> from sqlalchemy.orm import sessionmaker
>>> 
>>> Base = declarative_base()
>>> 
>>> class ModelBase:
>>> 
>>>   @classmethod
>>>   def create(cls, session, **kwargs):
>>> obj = cls(**kwargs)
>>> session.add(obj)
>>> session.commit()
>>> 
>>> return obj
>>> 
>>> 
>>> class User(ModelBase, Base):
>>>   __tablename__ = 'users'
>>> 
>>>   id = Column(Integer, primary_key=True)
>>> 
>>>   addresses = relationship("Address", order_by="Address.id", 
>>> back_populates="user")
>>> 
>>> class Address(ModelBase, Base):
>>>   __tablename__ = 'addresses'
>>> 
>>>   id = Column(Integer, primary_key=True)
>>> 
>>>   user_id = Column(Integer, ForeignKey('users.id'))
>>> 
>>>   user = relationship("User", back_populates="addresses")
>>> 
>>> 
>>> engine = create_engine('sqlite:///:memory:', echo=False)
>>> Base.metadata.create_all(engine)
>>> 
>>> Session = sessionmaker(bind=engine)
>>> session = Session()
>>> 
>>> jack = User.create(session)
>>> jack.addresses.append(Address.create(session))
>>> 
>>> -
>>> -
>>> I have tested the code with SQLAlchemy for 1.3.18 and 1.4.0b1. I'm using 
>>> python 3.9.1
>>> 
>>> When I rewrite the last few lines a bit the warning disappears. For 
>>> instance:
>>> 
>>> jack = User.create(session)
>>> a = Address.create(session)
>>> jack.addresses.append(a)
>>> 
>>> 
>>> Any insight is appreciated. I'm still learning.
>>> 
>>> Thanks,
>>> Christian
>>> 
>>> 
>>> 

>>> -- 
>>> 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.

Re: [sqlalchemy] Invalidated Collection

2021-02-03 Thread Christian Henning
Hi Mike,

thanks for your advice! I'll make the changes.

But let me ask you one thing. My classmethod create() is but more complex 
than I have posted. It's meant to catch IntegrityError so that unique 
constraints are enforced. Image a User table has a "unique" constraint on 
the name. When I try to create a user with the same name the create() would 
catch the exception and return the original user. I believe this only 
possible when all objects are committed to the database.

class ModelBase:

  @classmethod
  def create(cls, session, **kwargs):
obj = cls(**kwargs)
try:
  obj.save(session)
except IntegrityError:
  # print('HOLA - Integrity Error')

  session.rollback()
  obj = cls.find(session, **kwargs)

return obj

  def save(self, session):
session.add(self)
session.commit()

How would you solve the problem? Is there anything in the documentation?

Thanks,
Christian







On Wednesday, February 3, 2021 at 5:12:30 PM UTC-5 Mike Bayer wrote:

> the session.commit() method expires all attributes by default:
>
> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing
>
> your code is organized in an unusual way such that transactions are being 
> committed inside of attribute assignment operations:
>
> jack.addresses.append(Address.create(session))
>
> We can expand this out as follows:
>
> addr = jack.addresses
> address = Address()
> session.add(address)
> session.commit()   # <--- expires all attributes on "jack", "address", 
> invalidates "addr"
> attr.append(address)  # <--- this is invalid, transaction was already 
> commited
>
> I would strongly advise keeping session scope manage external to the set 
> of operations you are performing.  See 
> https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it
>  
> for guidelines.
>
>
> On Wed, Feb 3, 2021, at 2:27 PM, Christian Henning wrote:
>
> I don't understand why SQLAlchemy gives me the following warning:
>
> SAWarning: This collection has been invalidated.
>   util.warn("This collection has been invalidated.")
>
> Here is a minimal code example:
>
> -
> -
>
> import sqlalchemy
> print(sqlalchemy.__version__)
>
> from sqlalchemy import create_engine
> from sqlalchemy import Column, Integer, ForeignKey
>
> from sqlalchemy.exc import IntegrityError
> from sqlalchemy.ext.declarative import declarative_base
>
> from sqlalchemy.orm import relationship
> from sqlalchemy.orm import sessionmaker
>
> Base = declarative_base()
>
> class ModelBase:
>
>   @classmethod
>   def create(cls, session, **kwargs):
> obj = cls(**kwargs)
> session.add(obj)
> session.commit()
>
> return obj
>
>
> class User(ModelBase, Base):
>   __tablename__ = 'users'
>
>   id = Column(Integer, primary_key=True)
>
>   addresses = relationship("Address", order_by="Address.id", 
> back_populates="user")
>
> class Address(ModelBase, Base):
>   __tablename__ = 'addresses'
>
>   id = Column(Integer, primary_key=True)
>
>   user_id = Column(Integer, ForeignKey('users.id'))
>
>   user = relationship("User", back_populates="addresses")
>
>
> engine = create_engine('sqlite:///:memory:', echo=False)
> Base.metadata.create_all(engine)
>
> Session = sessionmaker(bind=engine)
> session = Session()
>
> jack = User.create(session)
> jack.addresses.append(Address.create(session))
>
> -
> -
> I have tested the code with SQLAlchemy for 1.3.18 and 1.4.0b1. I'm using 
> python 3.9.1
>
> When I rewrite the last few lines a bit the warning disappears. For 
> instance:
>
> jack = User.create(session)
> a = Address.create(session)
> jack.addresses.append(a)
>
>
> Any insight is appreciated. I'm still learning.
>
> Thanks,
> Christian
>
>
>
> -- 
> 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.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/7a369ff7-659c-4ee4-a8d0-a786ec3579aen%40googlegroups.com
>  
> 
> .
>
>
>

-- 
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 

Re: [sqlalchemy] Invalidated Collection

2021-02-03 Thread Mike Bayer
the session.commit() method expires all attributes by default:

https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing

your code is organized in an unusual way such that transactions are being 
committed inside of attribute assignment operations:

jack.addresses.append(Address.create(session))

We can expand this out as follows:

addr = jack.addresses
address = Address()
session.add(address)
session.commit()   # <--- expires all attributes on "jack", "address", 
invalidates "addr"
attr.append(address)  # <--- this is invalid, transaction was already 
commited

I would strongly advise keeping session scope manage external to the set of 
operations you are performing.  See 
https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it
 for guidelines.


On Wed, Feb 3, 2021, at 2:27 PM, Christian Henning wrote:
> I don't understand why SQLAlchemy gives me the following warning:
> 
> SAWarning: This collection has been invalidated.
>   util.warn("This collection has been invalidated.")
> 
> Here is a minimal code example:
> 
> -
> -
> 
> import sqlalchemy
> print(sqlalchemy.__version__)
> 
> from sqlalchemy import create_engine
> from sqlalchemy import Column, Integer, ForeignKey
> 
> from sqlalchemy.exc import IntegrityError
> from sqlalchemy.ext.declarative import declarative_base
> 
> from sqlalchemy.orm import relationship
> from sqlalchemy.orm import sessionmaker
> 
> Base = declarative_base()
> 
> class ModelBase:
> 
>   @classmethod
>   def create(cls, session, **kwargs):
> obj = cls(**kwargs)
> session.add(obj)
> session.commit()
> 
> return obj
> 
> 
> class User(ModelBase, Base):
>   __tablename__ = 'users'
> 
>   id = Column(Integer, primary_key=True)
> 
>   addresses = relationship("Address", order_by="Address.id", 
> back_populates="user")
> 
> class Address(ModelBase, Base):
>   __tablename__ = 'addresses'
> 
>   id = Column(Integer, primary_key=True)
> 
>   user_id = Column(Integer, ForeignKey('users.id'))
> 
>   user = relationship("User", back_populates="addresses")
> 
> 
> engine = create_engine('sqlite:///:memory:', echo=False)
> Base.metadata.create_all(engine)
> 
> Session = sessionmaker(bind=engine)
> session = Session()
> 
> jack = User.create(session)
> jack.addresses.append(Address.create(session))
> 
> -
> -
> I have tested the code with SQLAlchemy for 1.3.18 and 1.4.0b1. I'm using 
> python 3.9.1
> 
> When I rewrite the last few lines a bit the warning disappears. For instance:
> 
> jack = User.create(session)
> a = Address.create(session)
> jack.addresses.append(a)
> 
> 
> Any insight is appreciated. I'm still learning.
> 
> Thanks,
> Christian
> 
> 
> 

> -- 
> 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 view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/7a369ff7-659c-4ee4-a8d0-a786ec3579aen%40googlegroups.com
>  
> .

-- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/f16f3de5-b48d-4c89-9ebf-2430e65c579f%40www.fastmail.com.


[sqlalchemy] Invalidated Collection

2021-02-03 Thread Christian Henning
I don't understand why SQLAlchemy gives me the following warning:

SAWarning: This collection has been invalidated.
  util.warn("This collection has been invalidated.")

Here is a minimal code example:

-
-

import sqlalchemy
print(sqlalchemy.__version__)

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, ForeignKey

from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class ModelBase:

  @classmethod
  def create(cls, session, **kwargs):
obj = cls(**kwargs)
session.add(obj)
session.commit()

return obj


class User(ModelBase, Base):
  __tablename__ = 'users'

  id = Column(Integer, primary_key=True)

  addresses = relationship("Address", order_by="Address.id", 
back_populates="user")

class Address(ModelBase, Base):
  __tablename__ = 'addresses'

  id = Column(Integer, primary_key=True)

  user_id = Column(Integer, ForeignKey('users.id'))

  user = relationship("User", back_populates="addresses")


engine = create_engine('sqlite:///:memory:', echo=False)
Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

jack = User.create(session)
jack.addresses.append(Address.create(session))

-
-

I have tested the code with SQLAlchemy for 1.3.18 and 1.4.0b1. I'm using 
python 3.9.1

When I rewrite the last few lines a bit the warning disappears. For 
instance:

jack = User.create(session)
a = Address.create(session)
jack.addresses.append(a)


Any insight is appreciated. I'm still learning.

Thanks,
Christian


-- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/7a369ff7-659c-4ee4-a8d0-a786ec3579aen%40googlegroups.com.