OK so database-wise this is fine, you have Event -> EParticipation(Guide
or Member) -> Member and that's fine (though seems like "is_guide"
should be a Boolean, probably).
Where it's going wrong is how the association proxies are being used.
You have the is_guide=True|False thing inside the "creator", which is
fine, but that only affects what happens when you append a record, it
doesn't affect what the association proxy does when you read it. so
both of these association proxies are giving you the same list of objects.
The use case of filtering on read is a little awkward and we have to use
custom collection classes. Making it a little worse is that the
association proxy's system for using custom collection classes is even
more awkward. so below is a full proof of concept.
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext import associationproxy
from sqlalchemy.ext.associationproxy import association_proxy
import functools
Base = declarative_base()
class FilteredList(associationproxy._AssociationList):
def __init__(
self, is_guide, lazy_collection, creator, value_attr, parent):
# awkward.
getter, setter = parent._default_getset(
associationproxy._AssociationList)
super(FilteredList, self).__init__(
lazy_collection, creator, getter, setter, parent
)
self.is_guide = is_guide
# define all the "read" methods that need to do filtering here. The
# proxied collection is in self.col
def __iter__(self):
return (m.member for m in self.col if m.is_guide is self.is_guide)
def __contains__(self, other):
return other in [
m.member for m in self.col if m.is_guide is self.is_guide
]
class Event(Base):
__tablename__ = 'event'
id = Column(Integer, primary_key=True)
title = Column(String(64))
# association proxy of "event_participations" collection
# to "keyword" attribute
guides = association_proxy(
'event_participations', 'member',
proxy_factory=functools.partial(FilteredList, True),
creator=lambda m: EventParticipation(member=m, is_guide=True))
participants = association_proxy(
'event_participations', 'member',
proxy_factory=functools.partial(FilteredList, False),
creator=lambda m: EventParticipation(member=m, is_guide=False))
def __init__(self, name):
self.title = name
def __repr__(self):
return 'Event(%s)' % self.title
class EventParticipation(Base):
__tablename__ = 'event_participation'
event_id = Column(Integer, ForeignKey('
event.id'), primary_key=True)
member_id = Column(Integer, ForeignKey('
member.id'), primary_key=True)
is_guide = Column(Boolean)
# bidirectional attribute/collection of "event"/"event_participations"
event = relationship(
Event,
backref=backref("event_participations",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
member = relationship("Member")
def __init__(self, member=None, event=None, is_guide=None):
self.event = event
self.member = member
self.is_guide = is_guide
def __repr__(self):
return 'EventParticipation(%s, %s, %s)' % (
self.event, self.member, self.is_guide)
class Member(Base):
__tablename__ = 'member'
id = Column(Integer, primary_key=True)
name = Column(String(64))
def __repr__(self):
return 'Member(%s)' % repr(
self.name)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
event = Event('test')
for g in (Member(name='g1'), Member(name='g2')):
event.guides.append(g)
s.add(event)
s.commit()
print "-----------------------"
assert g in event.guides
for m in (Member(name='m1'), Member(name='m2')):
event.participants.append(m)
s.commit()
for p in s.query(EventParticipation).all():
print(p)
assert m in event.participants
assert g not in event.participants
>
> Adding guides and participants works as expected, but querying for the
> guides or participants only fails. Eg. in the following example the last
> test fails
> |
> event = Event('test')
> for g in (Member(name='g1'), Member(name='g2')):
> event.guides.append(g)
>
> db.session.add(event)
> db.session.commit()
>
> assert g in event.guides
>
> for m in (Member(name='m1'), Member(name='m2')):
> event.participants.append(m)
>
> db.session.commit()
>
> for p in EventParticipation.query.all():
> print(p)
>
> assert m in event.participants
> assert g not in event.participants
> |
>
> How can I specify the filter for the association proxy? Is this possible
> or is this database design just not good and I should change the data
> layout (If yes, what would be a good design?)
>
> Kind regards
>
> --
> 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
> <mailto:
sqlalchemy+...@googlegroups.com>.
> To post to this group, send email to
sqlal...@googlegroups.com
> <mailto:
sqlal...@googlegroups.com>.
> Visit this group at
https://groups.google.com/group/sqlalchemy.
> For more options, visit
https://groups.google.com/d/optout.