On Fri, Mar 8, 2019 at 3:05 PM Conferency <best....@gmail.com> wrote:
>
> Thanks.
>
> I didn't use association proxy.
>
> > "because you can't add Tool to Person.tools". This is real code and I can 
> > directly append the tool instance.

that will cause this error:

sqlalchemy.orm.exc.FlushError: Attempting to flush an item of type
<class '__main__.Keyword'> as a member of collection
"User.user_keywords". Expected an object of type <class
'__main__.UserKeyword'> or a polymorphic subclass of this type.


Feel free to send a complete working example along to show what you
are doing in full.



>
> class Association(db.Model):
>     ...
>     id = db.Column(Integer, primary_key=True, index=True)
>     tool_id = db.Column(Integer, ForeignKey('tools.id'))
>     user_id = db.Column(Integer, ForeignKey(users.id'))
>     user = db.relationship('User', back_populates='tool')
>     tool = db.relationship('Tool', back_populates='user')
>
>
>
> On Friday, March 8, 2019 at 8:34:53 AM UTC-5, Mike Bayer wrote:
>>
>> On Thu, Mar 7, 2019 at 4:46 PM Conferency <best...@gmail.com> wrote:
>> >
>> > Hi, I have 3 classes: two have many to many relationship between them, one 
>> > is association class.
>> >
>> > class Person:
>> >     ...
>> >     tools = relationship('Association', back_populates='user', lazy=True, 
>> > cascade='all, delete-orphan')
>> >
>> > class Tool:
>> >     ...
>> >    users = relationship('Association', back_populates='tool', lazy=True, 
>> > cascade='all, delete-orphan')
>> >
>> >
>> > One person object has 3 tools [<Tool id=1>, <Tool id=2>, <Tool id=3>]. 
>> > When I update the tool of the person I did
>> >
>> > person.tools = []
>> > for tool_id in [2, 4, 5]:
>> >     tool = Tool.query.get(tool_id)
>> >     if tool:
>> >         person.tools.append(tool)
>> >
>> >
>> > This operation fails, since when I set tools to an empty list, tool 1,2,3 
>> > are deleted, so only tool 4,5 are added into the list. If I remove the 
>> > delete-orphan in the cascade, I will have some redundant data in the 
>> > database. I can remove the unwanted tools from person.tools first instead 
>> > of setting it to empty list. I'd like to know if there is a simple way to 
>> > achieve same result. Thank you.
>>
>> that's not the real code because you can't add Tool to Person.tools,
>> it refers to an "Association" object which is not pictured.  Are you
>> using the association proxy?
>>
>> If I take the example from the docs at
>> https://docs.sqlalchemy.org/en/latest/orm/extensions/associationproxy.html#simplifying-association-objects,
>> I can run the same code you have and at the end the state is correct.
>>  It does delete the association objects that are removed, but it then
>> recreates them on the append pass.  If you want it to maintain the
>> association objects that are already there, you need to manipulate the
>> association objects directly.   Try out the script below.
>>
>> from sqlalchemy import Column, Integer, String, ForeignKey
>> from sqlalchemy.orm import relationship, backref
>>
>> from sqlalchemy.ext.associationproxy import association_proxy
>> from sqlalchemy.ext.declarative import declarative_base
>> from sqlalchemy import create_engine
>> from sqlalchemy.orm import Session
>>
>> Base = declarative_base()
>>
>>
>> class User(Base):
>>     __tablename__ = "user"
>>     id = Column(Integer, primary_key=True)
>>     name = Column(String(64))
>>
>>     # association proxy of "user_keywords" collection
>>     # to "keyword" attribute
>>     keywords = association_proxy("user_keywords", "keyword")
>>
>>     def __init__(self, name):
>>         self.name = name
>>
>>
>> class UserKeyword(Base):
>>     __tablename__ = "user_keyword"
>>     user_id = Column(Integer, ForeignKey("user.id"), primary_key=True)
>>     keyword_id = Column(Integer, ForeignKey("keyword.id"), primary_key=True)
>>     special_key = Column(String(50))
>>
>>     # bidirectional attribute/collection of "user"/"user_keywords"
>>     user = relationship(
>>         User, backref=backref("user_keywords", cascade="all, delete-orphan")
>>     )
>>
>>     # reference to the "Keyword" object
>>     keyword = relationship("Keyword")
>>
>>     def __init__(self, keyword=None, user=None, special_key=None):
>>         self.user = user
>>         self.keyword = keyword
>>         self.special_key = special_key
>>
>>
>> class Keyword(Base):
>>     __tablename__ = "keyword"
>>     id = Column(Integer, primary_key=True)
>>     keyword = Column("keyword", String(64))
>>
>>     def __init__(self, keyword):
>>         self.keyword = keyword
>>
>>     def __repr__(self):
>>         return "Keyword(%s)" % repr(self.keyword)
>>
>>
>> e = create_engine("sqlite://", echo=True)
>> Base.metadata.create_all(e)
>>
>> s = Session(e)
>>
>> u1 = User("foo")
>> k1, k2, k3, k4, k5 = (
>>     Keyword("k1"),
>>     Keyword("k2"),
>>     Keyword("k3"),
>>     Keyword("k4"),
>>     Keyword("k5"),
>> )
>>
>> u1.keywords = [k1, k2, k3]
>> s.add_all([u1, k1, k2, k3, k4, k5])
>> s.commit()
>>
>>
>> u1.keywords = []
>> for kname in ["k2", "k4", "k5"]:
>>     k = s.query(Keyword).filter_by(keyword=kname).one()
>>     u1.keywords.append(k)
>>
>> s.commit()
>>
>> print(u1.keywords)
>>
>>
>> output at the end:
>>
>> [Keyword('k2'), Keyword('k4'), Keyword('k5')]
>>
>>
>>
>>
>> >
>> > --
>> > 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 post to this group, send email to sqlal...@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.

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