On Mon, Jul 2, 2018 at 4:39 AM Juan <[email protected]> wrote: > > Hi, > > I do not know if this is that I am missing something in how SQLAlchemy > works or an issue; I have read the doc and searched for an explanation > but could not find an answer. > > When appending via an intermediate table, a M2M relationship behaves > differently wether I use the back_populates argument or not ( or use > backref) > > * Without using back_populates or backref is works fine > ``` > from sqlalchemy import Column, Integer, String, ForeignKey, Table > from sqlalchemy.orm import relationship > from sqlalchemy.ext.declarative import declarative_base > > Base = declarative_base() > > class User(Base): > __tablename__ = 'user' > id = Column(Integer, primary_key=True) > name = Column(String(64)) > keywords = relationship("UsersKeywords") > > def __init__(self, name): > self.name = name > > class Keyword(Base): > __tablename__ = 'keyword' > id = Column(Integer, primary_key=True) > keyword = Column('keyword', String(64)) > > def __init__(self, keyword): > self.keyword = keyword > > class UsersKeywords(Base): > __tablename__ = 'userskeywords' > user_id = Column( Integer, ForeignKey("user.id"), primary_key=True) > keyword_id = Column(Integer, ForeignKey("keyword.id"), > primary_key=True) > user = relationship(User, back_populates="keywords") > keyword = relationship(Keyword) > > >>>u = User(‘John’) > >>>u.keywords.append(Keyword(‘Python’) > >>>u.keywords > [<Keyword object at 0x10d09f860>] > ``` > > * With back_populates or backref in one of the ‘M’ sides of the > relation, ‘User’ in this case, I get a KeyError exception: > ``` > class User(Base): > __tablename__ = 'user' > id = Column(Integer, primary_key=True) > name = Column(String(64)) > keywords = relationship('UsersKeywords', back_populates='user') # > added back_populates argument > > def __init__(self, name): > self.name = name > > >>>u = User(‘John’) > >>>u.keywords.append(Keyword(‘Python’) > > Traceback (most recent call last): > File "<input>", line 1, in <module> > File > "/Users/xxx/Development/python_ws/flask_env/lib/python3.6/site-packages/sqlalchemy/orm/collections.py", > line 1044, in append > item = __set(self, item, _sa_initiator) > File > "/Users/xxx/Development/python_ws/flask_env/lib/python3.6/site-packages/sqlalchemy/orm/collections.py", > line 1016, in __set > item = executor.fire_append_event(item, _sa_initiator) > File > "/Users/xxx/Development/python_ws/flask_env/lib/python3.6/site-packages/sqlalchemy/orm/collections.py", > line 680, in fire_append_event > item, initiator) > File > "/Users/xxx/Development/python_ws/flask_env/lib/python3.6/site-packages/sqlalchemy/orm/attributes.py", > line 943, in fire_append_event > state, value, initiator or self._append_token) > File > "/Users/xxx/Development/python_ws/flask_env/lib/python3.6/site-packages/sqlalchemy/orm/attributes.py", > line 1210, in emit_backref_from_collection_append_event > child_impl = child_state.manager[key].impl > KeyError: 'user' > ``` > > Why is the behavior different?
Your "User.keywords" property is supposed to contain "UsersKeyword" instances (which have a "user" property), You've appended a "Keyword" instance to it, which *doesn't* have a "user" property, hence the error. If your userskeywords table only has foreign keys to the user and keyword tables, and doesn't have any other behaviour, you probably don't want to define it as a mapped class at all. Instead, use the Table constructor, as in the first example at http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#many-to-many. If you really do want to treat UsersKeywords as a mapped class in its own right, you need to ensure that those objects are constructed whenever you make a relationship between a user and a keyword: >>> u.keywords.append(UsersKeywords(keyword=keyword)) The association proxy (http://docs.sqlalchemy.org/en/latest/orm/extensions/associationproxy.html) can help with this. Hope that helps, Simon -- 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 [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
