Ok, I think the option with two assiociation tables is more sane.
Thank you for the advices.

Am Samstag, 18. März 2017 20:07:34 UTC+1 schrieb Mike Bayer:
>
> if you want an "easier" solution, it's just use two different 
> association tables, one for "participants" and one for "guides".   Then 
> make two relationships using a different argument for "secondary".   The 
> association object pattern is usually about some kind of information 
> that needs to travel along with the association (like an amount paid, or 
> a date, or something).   but in this case it's a kind of discriminator. 
>   using two separate tables / relationships is the easy way to get that. 
>
> On 03/18/2017 08:11 AM, Jürg Rast wrote: 
> > Just realised, there are more functions which need to be overwritten to 
> > get a completly working design. Functions such as __getslice__, __len__, 
> > __bool__, and many more need also a new implementation. 
> > 
> > It seems as the _AssociationCollection.col is just a getter for the 
> > lazy_collection. So is it possible to overwrite this getter somhow to 
> > automatically return the correct collection of objects? 
> > 
> > 
> >     Hey Mike, 
> > 
> >     thank you for the proof of concept! I tried it out and it seems to 
> >     work. However, as you said, it's a little akward... 
> > 
> >     I'm not a specialist with DB Design, but I think this pattern is 
> >     used at other places to. What would your recommondation be for this 
> >     kind of relationship between tables? As you said, database-wise this 
> >     is completly reasonable. But in the end I wan't a design which is 
> >     readable and maintainable in the long term. 
> > 
> >     Btw: Of course, the `is_guide` column should be a boolean, I just 
> >     forgot to change it from the example code. And I think a 
> >     AssociationSet would be a better fit, because a member can either 
> >     guide a event or participate a event, but not both on the same time. 
> > 
> > 
> >     Am Samstag, 18. März 2017 03:50:22 UTC+1 schrieb Mike Bayer: 
> > 
> > 
> > 
> >         On 03/17/2017 07:43 PM, Jürg Rast wrote: 
> >         > I followed the examples about association proxies 
> >         > 
> >         (
> http://docs.sqlalchemy.org/en/latest/orm/extensions/associationproxy.html 
> >         <
> http://docs.sqlalchemy.org/en/latest/orm/extensions/associationproxy.html>) 
>
> > 
> >         > but now I'm stuck. 
> >         > 
> >         > I have the following example (using flask + sqlalchemy) of a 
> >         simple 
> >         > event management: 
> >         > 
> >         > | 
> >         > from sqlalchemy import Column, String, Integer, ForeignKey, 
> >         Boolean 
> >         > from sqlalchemy.orm import relationship, backref 
> >         > from sqlalchemy.ext.associationproxy import association_proxy 
> >         > from . import db, BaseModel 
> >         > 
> >         > 
> >         > class Event(BaseModel): 
> >         >     __tablename__ = 'event' 
> >         >     title = Column(String(64)) 
> >         > 
> >         >     # association proxy of "event_participations" collection 
> >         >     # to "keyword" attribute 
> >         >     guides = association_proxy('event_participations', 
> 'member', 
> >         >                 creator=lambda m: EventParticipation(member=m, 
> >         > is_guide=True)) 
> >         > 
> >         >     participants = association_proxy('event_participations', 
> >         'member', 
> >         >                         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(db.Model): 
> >         >     __tablename__ = 'event_participation' 
> >         >     event_id = Column(Integer, ForeignKey('event.id 
> >         <http://event.id>'), primary_key=True) 
> >         >     member_id = Column(Integer, ForeignKey('member.id 
> >         <http://member.id>'), primary_key=True) 
> >         >     is_guide = Column(String(50)) 
> >         > 
> >         >     # 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(BaseModel): 
> >         >     __tablename__ = 'member' 
> >         >     name = Column(String(64)) 
> >         > 
> >         >     def __repr__(self): 
> >         >         return 'Member(%s)' % repr(self.name <http://self.name>) 
>
> >         > 
> >         > | 
> >         > 
> >         > So, basically a member can be a 'guide' for a event or a 
> >         'participant' 
> >         > and each event can have multiple guides and participants. 
> >         Therefore I 
> >         > created a secondary table which holds the event and member id 
> >         and also a 
> >         > field which indicates if the member has registered as member 
> >         or as guide. 
> > 
> >         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 
> >         <http://event.id>'), primary_key=True) 
> >              member_id = Column(Integer, ForeignKey('member.id 
> >         <http://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 <http://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 
> >         <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+unsubscr...@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 
> >         <https://groups.google.com/group/sqlalchemy>. 
> >         > For more options, visit https://groups.google.com/d/optout 
> >         <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 
> >     <http://stackoverflow.com/help/mcve> for a full description. 
> >     --- 
> >     You received this message because you are subscribed to a topic in 
> >     the Google Groups "sqlalchemy" group. 
> >     To unsubscribe from this topic, visit 
> >     https://groups.google.com/d/topic/sqlalchemy/nUNerrokPVE/unsubscribe 
> >     <
> https://groups.google.com/d/topic/sqlalchemy/nUNerrokPVE/unsubscribe>. 
> >     To unsubscribe from this group and all its topics, send an email to 
> >     sqlalchemy+unsubscr...@googlegroups.com 
> >     <mailto:sqlalchemy+unsubscr...@googlegroups.com>. 
> >     To post to this group, send email to sqlalchemy@googlegroups.com 
> >     <mailto:sqlalchemy@googlegroups.com>. 
> >     Visit this group at https://groups.google.com/group/sqlalchemy 
> >     <https://groups.google.com/group/sqlalchemy>. 
> >     For more options, visit https://groups.google.com/d/optout 
> >     <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 
> > <mailto:sqlalchemy+unsubscr...@googlegroups.com>. 
> > To post to this group, send email to sqlalchemy@googlegroups.com 
> > <mailto:sqlalchemy@googlegroups.com>. 
> > 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