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.

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 <javascript:>> 
> 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 <javascript:>. 
> > To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:>. 
> > 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