On Mon, Jul 24, 2017 at 6:01 PM, Ruben Di Battista
<rubendibatti...@gmail.com> wrote:
> Would cause again an IntegrityError. So I modified the recipe like this:
>    @event.listens_for(Session, "after_attach")
>    def after_attach(session, instance):
>        # when UserCourse objects are attached to a Session,
>        # figure out what Course in the database it should point to,
>        # or create a new one.
>        if isinstance(instance, UserCourse):
>            cache = getattr(session, _unique_cache)
>            if cache is None:
>                session._unique_cache = {}
>            with session.no_autoflush:
>                key = instance._course_title
>                course = session.query(Course).filter_by(
>                                        title=key).first()
>                if course is None:
>                    # Here we check that the object is in the session (but
> not committed yet
>                    # in the db
>                    if key in cache:
>                        course = cache[key]
>                    else:
>                    course = Course(title=instance._course_title)
>                    cache[key] = course
>                instance.course = course
>
>
> Don't know if there's a better way of achieving this.

I'd have to find some time to re-familiarize with this but in general
if you have it working the way you want, you're probably good :)


>
> On Thursday, January 9, 2014 at 1:44:00 AM UTC+1, Michael Bayer wrote:
>>
>> OK well to do it exactly the way the example does it, each time we create
>> a UserCourse, it will also create a Course.  That’s pretty simple, we use
>> two association proxies, one for User.courses and the other for
>> UserCourse.course, mappings are like this:
>>
>> class User(Base):
>>     __tablename__ = 'users'
>>
>>     # Columns
>>     id = Column(Integer, primary_key=True)
>>     name = Column(Text)
>>
>>     # Relations
>>     courses = association_proxy('user_courses', 'grade',
>>         creator=lambda k, v: UserCourse(course_title=k, grade=v))
>>
>>     def __init__(self, name):
>>         self.name = name
>>
>> class Course(Base):
>>     __tablename__ = 'courses'
>>
>>     # Columns
>>     id = Column(Integer, primary_key=True)
>>     title = Column(Text, unique=True)
>>
>>     def __init__(self, title):
>>         self.title = title
>>
>>
>> # Composite association proxies linking users and preferences
>> class UserCourse(Base):
>>     __tablename__ = 'user_courses'
>>
>>     # Columns
>>     user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
>>     course_id = Column(Integer, ForeignKey(Course.id), primary_key=True)
>>     grade = Column(Integer)
>>
>>     # Relations
>>     user = relationship(
>>         User,
>>         backref=backref(
>>             'user_courses',
>>             collection_class=attribute_mapped_collection('course_title'),
>>             cascade='all, delete-orphan'
>>             )
>>         )
>>     course = relationship(Course)
>>
>>     course_title = association_proxy("course", "title”)  # will create a
>> new Course object when course_title is set
>>
>>     def __init__(self, course_title, grade):
>>         self.course_title = course_title
>>         self.grade = grade
>>
>> the other way that’s maybe a little more “real world” is that if two
>> different UserCourse objects are for “math”, we’d want only one Course
>> object with “math”.  There’s a few ways to go about making those unique
>> Course objects - one common one is the “unique object” recipe at
>> http://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject .
>>
>> A variant on that which I’ve been using lately doesn’t rely upon any kind
>> of global session and instead uses events.    In this approach, we modify
>> the above so that UserCourse.course_title temporarily points to a plain
>> string, then when attached to a Session looks up and/or creates the unique
>> Course object, looks like this:
>>
>> from sqlalchemy import event
>>
>> # same User and Course...
>>
>> # Composite association proxies linking users and preferences
>> class UserCourse(Base):
>>     __tablename__ = 'user_courses'
>>
>>     # Columns
>>     user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
>>     course_id = Column(Integer, ForeignKey(Course.id), primary_key=True)
>>     grade = Column(Integer)
>>
>>     # Relations
>>     user = relationship(
>>         User,
>>         backref=backref(
>>             'user_courses',
>>             collection_class=attribute_mapped_collection('course_title'),
>>             cascade='all, delete-orphan'
>>             )
>>         )
>>     course = relationship(Course)
>>
>>     def __init__(self, course_title, grade):
>>         self._course_title = course_title  # temporary, will turn into a
>>                                            # Course when we attach to a
>> Session
>>         self.grade = grade
>>
>>     @property
>>     def course_title(self):
>>         if self.course is not None:
>>             return self.course.title
>>         else:
>>             return self._course_title
>>
>>     @event.listens_for(Session, "after_attach")
>>     def after_attach(session, instance):
>>         # when UserCourse objects are attached to a Session,
>>         # figure out what Course in the database it should point to,
>>         # or create a new one.
>>         if isinstance(instance, UserCourse):
>>             with session.no_autoflush:
>>                 course = session.query(Course).filter_by(
>>
>> title=instance._course_title).first()
>>                 if course is None:
>>                     course = Course(title=instance._course_title)
>>                 instance.course = course
>>
>>
>> with either of these, a simple test run is like:
>>
>> e = create_engine("sqlite://", echo=True)
>> Base.metadata.create_all(e)
>>
>> s = Session(e)
>>
>> user = User(name='u1')
>> s.add(user)
>> s.commit()
>>
>> user.courses['math'] = 100
>> s.commit()
>>
>> assert user.courses['math'] == 100
>>
>>
>>
>>
>>
>> On Jan 8, 2014, at 6:32 PM, Brian Findlay <brian.m...@gmail.com> wrote:
>>
>> Hi, all. I've been trying to modify the example of a composite association
>> proxy
>> (http://www.sqlalchemy.org/docs/orm/extensions/associationproxy.html#composite-association-proxies)
>> to fit my needs.
>>
>> In the documentation example, there is a User object, a Keyword object,
>> and a UserKeyword association object that stores a 'special_key' for each of
>> a user's keywords. In the provided example, the result is a collection of
>> dictionaries where the 'special_key' is the key and the 'keyword' is the
>> value. I'm trying to inverse that mapping.
>>
>> In my particular use case (which I've simplified so as to make it as clear
>> as possible...I hope), I have a User object (a student), a Course object (an
>> academic course), and a UserCourse association object that stores each
>> user's grade for each course. My goal is to be able to set a student's grade
>> something like this:
>>
>> user.course['math'] = 100
>>
>> This is what I've come up with, but it (obviously) isn't working yet.
>>
>>
>> from sqlalchemy import Column, Integer, Text, ForeignKey
>> from sqlalchemy.ext.associationproxy import association_proxy
>> from sqlalchemy.ext.declarative import declarative_base
>> from sqlalchemy.orm.collections import attribute_mapped_collection
>> from sqlalchemy.orm import scoped_session, sessionmaker, relationship,
>> backref
>>
>>
>> Base = declarative_base()
>>
>>
>> class User(Base):
>>     __tablename__ = 'users'
>>
>>     # Columns
>>     id = Column(Integer, primary_key=True)
>>     name = Column(Text)
>>
>>     # Relations
>>     courses = association_proxy(
>>         'user_courses',
>>         'course',
>>         creator=lambda k, v: UserCourse(course=k, grade=v)
>>         )
>>
>>     def __init__(self, name):
>>         self.name = name
>>
>>
>> class Course(Base):
>>     __tablename__ = 'courses'
>>
>>     # Columns
>>     id = Column(Integer, primary_key=True)
>>     title = Column(Text, unique=True)
>>
>>     def __init__(self, title):
>>         self.title = title
>>
>>
>> # Composite association proxies linking users and preferences
>> class UserCourse(Base):
>>     __tablename__ = 'user_courses'
>>
>>     # Columns
>>     user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
>>     course_id = Column(Integer, ForeignKey(Course.id), primary_key=True)
>>     grade = Column(Integer)
>>
>>     # Relations
>>     user = relationship(
>>         User,
>>         backref=backref(
>>             'user_courses',
>>             collection_class=attribute_mapped_collection('grade'),
>>             cascade='all, delete-orphan'
>>             )
>>         )
>>     c = relationship('Course')
>>     course = association_proxy('c', 'title')
>>
>>
>>
>> I'd really appreciate anyone's help here, even if it's just showing me how
>> to modify the example in the documentation.
>>
>> --
>> 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.
>> To post to this group, send email to sqlal...@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
> --
> 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.

-- 
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