It isn't clear from the documentation how does the "collection_class" attribute work in a many-to-many relationship
From the documentation on the site, it isn't clear how to create a many-to-many relation where both sides hold their relations in a user defined "collection_class". The doc doesn't mention if the "collection_class" attribute affects only one side of the relation or both.
For example, let's look on the following code:
import sqlalchemy
from sqlalchemy import *
from sqlalchemy.orm import *
engine = create_engine('sqlite://')
metadata = MetaData()
metadata.bind = engine
Session = scoped_session(sessionmaker(bind = engine, autoflush = True,
autocommit = False))
# TABLES
owners_table = Table('owners', metadata,
Column('owner_id', Integer, primary_key=True),
)
dogs_table = Table('dogs', metadata,
Column('dog_id', Integer, primary_key=True),
)
owners2dogs_table = Table('owners2dogs', metadata,
Column('owner_id', Integer, ForeignKey(owners_table.columns.owner_id)),
Column('dog_id', Integer, ForeignKey(dogs_table.columns.dog_id)))
metadata.create_all()
# POJOS
class Owner(object):
pass
class Dog(object):
pass
# MAPPERS
owners2dogs_relation = relation(Dog,
backref = "owners",
secondary = owners2dogs_table,
collection_class = set)
mapper(Owner, owners_table, properties = {"dogs" : owners2dogs_relation})
mapper(Dog, dogs_table)
owner = Owner()
dog = Dog()
print type(owner.dogs)
print type(dog.owners)
The printed result is:
<class 'sqlalchemy.orm.collections.InstrumentedSet'>
<class 'sqlalchemy.orm.collections.InstrumentedList'>
and not:
<class 'sqlalchemy.orm.collections.InstrumentedSet'>
<class 'sqlalchemy.orm.collections.InstrumentedSet'>
I am not sure what is the anticipated result for most users. I can say for myself, that I thought that both sides would turn out to be a set, and I was wrong. Either way, it isn't mentioned anywhere - not in the site documentation and not in the code documentation.
The only way I could think of to make both sides keep their relation in a set is to do something like:
owners2dogs_relation = relation(Dog,
back_populates = "owners",
secondary = owners2dogs_table,
collection_class = set)
mapper(Owner, owners_table, properties = {"dogs" : owners2dogs_relation})
dogs2owners_relation = relation(Owner,
back_populates = "dogs",
secondary = owners2dogs_table,
collection_class = set)
mapper(Dog, dogs_table, properties = {"owners" : dogs2owners_relation})
If it's the answer, it should be documented too.
kobipe3@gmail.com
Comments (8)
-
repo owner -
repo owner i think the docstring for
backref()
should include a list of what attributes are copied over from the parent property automatically. thats something we should clarify. -
Account Deleted I'm so embarressed :) It's probably because most (or all, I'm not sure) of the examples containing a backref, didn't contain any other arguments for the backref function, other than the relation name. That's why I didn't even bother in checking the backref options. (and moved to check relation options instead) So my solution - put more and richer backref examples in the orm tutorial.
And I still like my workaround. It makes all the mapping more symmetrical.
-
- changed component to documentation
- changed milestone to 0.5.xx
-
Account Deleted docstring updated in orm/init.py:relationship https://bitbucket.org/olduvaihand/sqlalchemy/changeset/31338e8f84b9
-
repo owner need public access on this repository
-
repo owner - changed status to wontfix
no access on the repo here and this ticket is very old; I think the current description of backref() is fine, people seem to get it.
-
repo owner - changed milestone to 1.x.xx
- Log in to comment
whats wrong with
?
backref()
as a function is in the ORM tutorial and the API reference, and is hyperlinked in the description ofrelation()
too in the description of thebackref
keyword argument. its not nearly as obscure as "back_populates" so its funny you found that.