On Mar 27, 2014, at 9:27 AM, Darin Gordon <dar...@gmail.com> wrote:

> Let me preface this by saying that I researched to the best of my abilities 
> the answers to my following inquiries.  I would not ask for help without 
> first trying to investigate this on my own.  Asking through this forum is the 
> proverbial end of the line, so thanks for taking any time to read and respond.
> 
> I understand relational databases and have been bridging a knowledge gap with 
> ORMs, using SQLAlchemy.  I've struggled a bit while learning what exactly is 
> going on in the ORM when ADDING new associations in a M to N relationship.   
> 
> My questions are related to SQL Alchemy ORM concepts:
>       1) Relationships
>       2) Association Object pattern
>       3) Association Proxy
> 
> 
> To help me grasp the concepts, I pieced together a working example of an M to 
> N relation and drew a diagram showing the relationships.   The scenario is of 
> a very crude appointment scheduler that someone like a realtor may use to 
> keep track of who is visiting what property.  My attribute naming convention 
> was chosen to facilitate our discussion.  Link:   Github repo
> 
> There are two files of interest:
> orm_associations.py -- a working example 
> "SQLAlchemy - Association Object Relations.pdf" --  contains a diagram of the 
> M to N relationship in orm_associations.py
> 
> In this example, a user can be added to a meeting OR a meeting can be added 
> to a user using either of these association proxies:
> 
>                       Meeting.attendees_proxy.append(User)
>                                               or
>                       User.meetings_proxy.append(Meeting)
> 
> These statements create the desired outcome-- a new Appointment record 
> inserted into the association table and possibly a new user or meeting record 
> if none already exist.
> 
> I'd like to know what exactly is happening in the ORM when either of these 
> statements is executed.
>       
> For instance, what is the association_proxy creator doing?  If I add a user 
> to a meeting, the attendees_proxy creator is defined as follows:
>                                       creator=lambda user: 
> Appointment(appointment_user_relation=user))
>       
> The creator expression appears to be creating a new Appointment object, but 
> then defines the RELATIONSHIP as a User object?  I'm missing something here.  
> No idea what is going on.  It seems to be all a bit to automagical to me.

I'd note that when you append an item to a collection, or do anything else with 
your objects, not very much happens inside the ORM except that your object 
records that "something changed".  It's only at flush time that SQL types of 
things actually occur.   This is under the realm of understanding how the unit 
of work works, and for that I'd recommend my talk "The SQLAlchemy Session - In 
Depth" which is at 
http://techspot.zzzeek.org/2012/11/14/pycon-canada-the-sqlalchemy-session-in-depth/,
 and in particular you can run the linked "html demo" yourself, which will 
painstakingly graphically draw out every single state change within the 
session/unit of work/object graph if you really want to step through it.

then lets talk about the association proxy.  this is an entirely different 
subject, the association proxy has relatively little that's specific to the 
ORM, and is just working with Python attributes in such a way as to automate 
the "middle".  The following code example illustrates the main idea of what 
association proxy does, with no ORM or SQLAlchemy at all.  You can copy this to 
a .py script and run it.  What we're making use of here are Python magic 
methods; some introductory material on the ones specific to sequences are at: 
http://www.rafekettler.com/magicmethods.html#sequence .    If you understand 
this script then you know 99% of what the association proxy is doing:

class AssocProxy(object):
    def __init__(self, collection_name, proxied):
        self.collection_name = collection_name
        self.proxied = proxied

    def __get__(self, obj, cls):
        if obj is not None:
            return ProxyList(self, getattr(obj, self.collection_name))

    def _makeitem(self, item):
        return Association(**{self.proxied: item})

class ProxyList(object):
    def __init__(self, parent, collection):
        self.parent = parent
        self.collection = collection

    def __getitem__(self, index):
        item = self.collection[index]
        item = getattr(item, self.parent.proxied)
        return item

    def append(self, item):
        self.collection.append(self.parent._makeitem(item))

    def __setitem__(self, index, item):
        self.collection[index] = self.parent._makeitem(item)

    def __iter__(self):
        return (getattr(item, self.parent.proxied) for item in self.collection)

class Parent(object):
    def __init__(self):
        self.associations = []

    children = AssocProxy('associations', 'element')

class Association(object):
    def __init__(self, element):
        self.element = element

class Child(object):
    pass


p1 = Parent()
p1.children.append(Child())
p1.children.append(Child())

print list(p1.children)

print list(p1.associations)


-- 
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 http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to