Can child-side of a one-to-many create the parent side?

12 views
Skip to first unread message

Gregg Lind

unread,
Aug 14, 2009, 6:02:23 PM8/14/09
to sqlal...@googlegroups.com
I have a many-to-one relation, let's say between Address and User, both created using Declarative. 

I would like it that if an Address references a User, it will create that User, if it doesn't exist.  Otherwise, it should use the existing one.  There should be no addresses in the table that don't have users.  *It could be that my whole sense of modeling is fouled up here, and I appreciate correction.* 

1.  Is this possible using the current _cascade_ tools?  Is so, how is it set up?
2.  If not, then what is the best way to handle this? 
3.  From the docs for "relation" it seems like the cascading is all viewed from the "parent" (one) side, is that correct?

Thanks!

Gregg

-----------------------------------------------------

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relation, backref

Base = declarative_base()
class User(Base):
    __tablename__ = 'users'
    name = Column(String,primary_key=True)
   
    def __init__(self, name):
        self.name = name
   
    def __repr__(self):
       return "<User('%s')>" % (self.name)

class Address(Base):
    __tablename__ = 'addresses'
    email_address = Column(String, nullable=False, primary_key=True)
    user_name = Column(ForeignKey('users.name'),nullable=False, primary_key=True)
    user = relation(User, backref=backref('addresses', order_by=email_address), cascade='all')
   
    def __init__(self, email_address,uname=None):
        self.email_address = email_address
        self.user_name=uname
   
    def __repr__(self):
        return "<Address('%s'), for User('%s')>" % (self.email_address, self.user_name)

def db_setup(connstring='sqlite:///:memory:', echo=False):
    engine = create_engine(connstring, echo=echo)
    session = sessionmaker(bind=engine, autoflush=False, autocommit=False)()
    Base.metadata.bind = engine
    Base.metadata.create_all()
    return session, engine


session,engine=db_setup('postgres:///test_test',True)
A = Address("me@place","Daniel Waterhouse")
session.add(A)
session.commit()

# fails because no User named Daniel Waterhouse?  I want one created.  The user_name is enough info to do so.
# otherwise, use the existing one.
----------------------------------

Michael Bayer

unread,
Aug 14, 2009, 6:20:49 PM8/14/09
to sqlal...@googlegroups.com
Gregg Lind wrote:
> I have a many-to-one relation, let's say between Address and User, both
> created using Declarative.
>
> I would like it that if an Address references a User, it will create that
> User, if it doesn't exist. Otherwise, it should use the existing one.
> There should be no addresses in the table that don't have users. *It
> could
> be that my whole sense of modeling is fouled up here, and I appreciate
> correction.*
>
> 1. Is this possible using the current _cascade_ tools? Is so, how is it
> set up?

no, since you're talking about instantiating an object that doesn't exist
otherwise.

> 2. If not, then what is the best way to handle this?

the constructor of your Address says, "self.user = User(<args you want>)".
If that's too early, you can write a SessionExtension.before_flush()
that scans through and ensures all Address objects have a default User()
as desired.

> 3. From the docs for "relation" it seems like the cascading is all viewed
> from the "parent" (one) side, is that correct?

all relation()s feature cascading. A two-way relationship consists of
two separate relation()s, "backref" is just a shortcut to creating them
and linking them together. "parent" and "child" are relative to which
relation() you're referring to.

Gregg Lind

unread,
Aug 14, 2009, 6:33:51 PM8/14/09
to sqlal...@googlegroups.com
One quick follow up.

re: question 2:  If I create a User there, and that user exists in the DB, won't that cause problems?  I ask because this situation seems analogous to a "create if not existing" situation. 

Thanks for the (as ever) fast response.

GL

Michael Bayer

unread,
Aug 14, 2009, 6:45:42 PM8/14/09
to sqlal...@googlegroups.com
Gregg Lind wrote:
> One quick follow up.
>
> re: question 2: If I create a User there, and that user exists in the DB,
> won't that cause problems? I ask because this situation seems analogous
> to
> a "create if not existing" situation.

well sure if your Address implies a particular User that might already
exist then yeah you have to try loading it first. or use merge() if you
can deterministically generate the primary key (does the same thing
basically).

Reply all
Reply to author
Forward
0 new messages