I have two models, Word
and Sentence
, which have a bidirectional many-to-many relationship. In order to store additional information, I have an association object, WordInSentence
.
class WordInSentence(Base):
__tablename__ = "word_in_sentence"
word_id = Column(Integer, ForeignKey('word.id'),
primary_key=True)
sentence_id = Column(Integer, ForeignKey('sentence.id'),
primary_key=True)
space_after = Column(String)
tag = Column(String)
position = Column(Integer)
word = relationship("Word",
backref=backref("word_sentences", lazy="dynamic"))
sentence = relationship("Sentence",
backref=backref("sentence_words", lazy="dynamic"))
class Sentence(Base):
text = Column(Text, index = True)
words = association_proxy("sentence_words", "word",
creator=lambda word: WordInSentence(word=word))
class Word(Base):
word = Column(String, index = True)
sentences = association_proxy("word_sentences", "sentence",
creator=lambda sent: WordInSentence(sentence=sent))
def __repr__(self):
return "<Word: " + str(self.word) + ">"
I want to be able to do things like this:
w = Word()
s = Sentence()
w.sentences = [s]
However, I get errors like this:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/plasma/project/venv/lib/python2.7/site-packages/sqlalchemy/ext/associationproxy.py", line 274, in __set__
proxy.clear()
File "/home/plasma/project/venv/lib/python2.7/site-packages/sqlalchemy/ext/associationproxy.py", line 629, in clear
del self.col[0:len(self.col)]
TypeError: object of type 'AppenderBaseQuery' has no len()
I also noticed this example in the docs, but I'm not sure how to make it bidirectional and a list.
The reason you were getting the error TypeError: object of type 'AppenderBaseQuery' has no len()
is because your relationships are set to lazy="dynamic"
. If you actually inspect the object, it is simply an SQL query. That's why you can't iterate over it - it has to be executed first.
You can do that by using the standard functions available to all queries - calling filter(<conditions>)
on the object, or all()
if you want everything.
If you don't want the extra step of executing a dynamic query every time you access it, another option -- if the number of child items in the relationship is not large -- is to change the lazy
setting to 'select'
. This will then run the association query at the same time as the query for the parent object, which is of course impractical for a huge child database, but not unreasonable for a smaller one. Then it can be iterated over as you expect.
Hopes this fragment of code can help you. I'm just changing the creation sequence and adding engine and session. Here the working code.
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm.session import Session
from sqlalchemy.sql.schema import Column, ForeignKey
from sqlalchemy.sql.sqltypes import Integer, String, Float, Date
Base = declarative_base()
class Sentence(Base):
__tablename__ = 'sentence'
id = Column(Integer, primary_key=True)
text = Column(String, index = True)
sentence_words = relationship('WordInSentence')
words = association_proxy("sentence_words", "word")
class Word(Base):
__tablename__ = 'word'
id = Column(Integer, primary_key=True)
word = Column(String, index = True)
word_sentences = relationship('WordInSentence')
sentences = association_proxy("word_sentences", "sentence")
def __repr__(self):
return "<Word: " + str(self.word) + ">"
class WordInSentence(Base):
__tablename__ = "word_in_sentence"
word_id = Column(Integer, ForeignKey(Word.id),
primary_key=True)
sentence_id = Column(Integer, ForeignKey(Sentence.id),
primary_key=True)
space_after = Column(String)
tag = Column(String)
position = Column(Integer)
word = relationship(Word)
#backref=backref("word_sentences", lazy="dynamic"))
sentence = relationship(Sentence)
#backref=backref("sentence_words", lazy="dynamic"))
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
session = Session(engine)
Test:
>>>
>>> w = Word(word='something new')
>>> w.sentences = [Sentence(text='there is somethine new')]
>>> session.add(w)
>>> session.commit()
>>> session.query(WordInSentence).one().word_id
1
>>> session.query(Word).one().word
'something new'
>>> session.query(Sentence).one().text
'there is somethine new'
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With