Michael, thanks, as ever, for your help.

So, I think I've managed to specify the relationships:

>>>
j = join(movies_table,md_table).join(directors_table).join(genres_table)
js = j.select(use_labels=True).alias('mdg')

r0 = relationship(Movies,
                  primaryjoin=(js.c.movies_movie_id==movies_table.c.movie_id),
                  foreign_keys=([js.c.movies_movie_id])
                  )
r1 = relationship(Directors,

primaryjoin=(js.c.directors_director_id==directors_table.c.director_id),
                  foreign_keys=([js.c.directors_director_id])
                  )
r2 = relationship(Genres,
                  primaryjoin=(js.c.genres_genre_id==genres_table.c.genre_id),
                  foreign_keys=([js.c.genres_genre_id])
                  )

mapper(MoviesAndDirectorsAndGenres,
       js,
       properties={'movie':r0,
                   'director':r1,
                   'genre':r2
                   },
        passive_updates=False)


To test it, I'm trying to create a new composite object, based on a
new movie but existing director & genre:


>>>
session=Session()

m_new = Movies()
m_new.id=8
m_new.title = 'new movie'
session.add(m_new)

d2 = session.query(Directors).get(2)
print '***************director=',d2
g6 = session.query(Genres).get(6)
print '***************genre=',g6

oo_new = MoviesAndDirectorsAndGenres()
oo_new.movie = m_new
oo_new.director = d2
oo_new.genre = g6

try:
#    another_new = session.merge(new)
    session.merge(oo_new)

but sqla is doing some really weird stuff:

2010-07-19 16:56:01,877 INFO sqlalchemy.engine.base.Engine.0x...37f0 BEGIN
2010-07-19 16:56:01,878 INFO sqlalchemy.engine.base.Engine.0x...37f0 INSERT INTO
 movies (movie_id, title, description, genre_id, release_date) VALUES
(?, ?, ?,?, ?)
2010-07-19 16:56:01,880 INFO sqlalchemy.engine.base.Engine.0x...37f0
(8, 'new movie', None, None, None)
2010-07-19 16:56:01,882 INFO sqlalchemy.engine.base.Engine.0x...37f0
SELECT directors.director_id AS directors_director_id, directors.name
AS directors_name
FROM directors
WHERE directors.director_id = ?
2010-07-19 16:56:01,885 INFO sqlalchemy.engine.base.Engine.0x...37f0 (2,)
***************director= <test_multitable.Directors object at 0x021E2030>
2010-07-19 16:56:01,888 INFO sqlalchemy.engine.base.Engine.0x...37f0
SELECT genres.genre_id AS genres_genre_id, genres.name AS genres_name,
genres.description AS genres_description
FROM genres
WHERE genres.genre_id = ?
2010-07-19 16:56:01,891 INFO sqlalchemy.engine.base.Engine.0x...37f0 (6,)
***************genre= <test_multitable.Genres object at 0x021E21B0>
2010-07-19 16:56:01,894 INFO sqlalchemy.engine.base.Engine.0x...37f0
INSERT INTO genres (genre_id, name, description) VALUES (?, ?, ?)
2010-07-19 16:56:01,895 INFO sqlalchemy.engine.base.Engine.0x...37f0
(6, None, None)
2010-07-19 16:56:01,898 INFO sqlalchemy.engine.base.Engine.0x...37f0 ROLLBACK

attempted to add new composite object, failed with  (IntegrityError)
PRIMARY KEY must be unique u'INSERT INTO genres (genre_id, name,
description) VALUES (?, ?, ?)' (6, None, None)

Traceback (most recent call last):
  File "test_multitable.py", line 105, in <module>
    session.commit()
  File "sqlalchemy\orm\session.py", line 653, in commit
    <etc etc>
IntegrityError: (IntegrityError) PRIMARY KEY must be unique u'INSERT INTO genres
 (genre_id, name, description) VALUES (?, ?, ?)' (6, None, None)


it seems to trying to insert a new genre, even though I've told it to
use an existing one?

I suppose, once I've done the insert into the movies table, i could
retrieve the new composite object via a session.query, instead of
trying to do a session.merge.  but this wouldn't work for creating a
new Director object, since I also need to create an entry into the
bridging table, something i'm hoping sqla could manage for me...

rgds,
hp


On Mon, Jul 19, 2010 at 3:31 PM, Michael Bayer <mike...@zzzcomputing.com> wrote:
>
> On Jul 19, 2010, at 10:24 AM, Harry Percival wrote:
>
>> OK, so I will treat any classes mapped to a join of multiple tables as
>> being a read-only API, and manually manage the write-API using
>> relationship().
>>
>> It doesn't look like I can define a relationship from the composite
>> mapped class to individual tables though?
>>
>>>>>
>> #single table mappers:
>> mapper(Movies,movies_table)
>> mapper(Directors,directors_table)
>> mapper(Genres,genres_table)
>>
>>
>> j = join(movies_table,md_table).join(directors_table).join(genres_table)
>> r0 = relationship(Movies,
>>                  primaryjoin=(movies_table.c.movie_id==md_table.c.movie_id),
>>                  foreign_keys=([md_table.c.movie_id]))
>> r1 = 
>> relationship(Directors)#,primaryjoin=(and_(movies_table.c.movie_id==md_table.c.movie_id,md_table.c.director_id==directors_table.c.director_id)))#,secondaryjoin=(md_table.c.director_id==directors_table.c.director_id),secondary=md_table)
>> r2 = relationship(Genres)
>>
>> mapper(MoviesAndDirectorsAndGenres,
>>       j.select(use_labels=True).alias('mdg'),
>
> j.select(...).alias() is the selectable that you have to define all your 
> primaryjoins against.   So you need to assign that on the outside to a 
> variable first.
>
>
>
>
>
>>       properties={'movie':r0,
>>                   'director':r1,
>>                   'genre':r2
>>                   },
>>        passive_updates=False)
>>
>> Tried lots of permutations of this, but it doesn't look like I can get
>> sqla to understand a 1-to-1 relationship between the
>> multiple-table-mapped MoviesAndDirectorsAndGenres objects and the
>> Movies, Directors, or Genres objects they're built out of...
>>
>> I think perhaps I'm just trying to do something that SQLA really
>> wasn't designed for...
>>
>> hp
>>
>> On Mon, Jul 19, 2010 at 2:29 AM, Michael Bayer <mike...@zzzcomputing.com> 
>> wrote:
>>>
>>> On Jul 15, 2010, at 5:11 PM, Harry Percival wrote:
>>>
>>>> thanks Michael. I really appreciate your help.
>>>>
>>>> How should use .merge()?  I've tried both:
>>>>>>>
>>>> another_new = MoviesAndDirectorsAndGenres() #init another_new as blank row
>>>> another_new = session.merge(new) #attempt to merge with my 'new'
>>>> object that has desired attributes
>>>>
>>>> and
>>>>>>>
>>>> new = session.merge(another_new) #attempt to merge blank object to my
>>>> new object with desired attributes
>>>>
>>>> the former fails trying to add a genre row that already exists, the
>>>> latter fails trying to add an object with all null values.
>>>>
>>>> the thing is, I'm really not trying to merge anything.  I'm trying to
>>>> create a new object, which happens to be made up partially of new
>>>> entries in some tables, and existing entries in other tables.
>>>> genres_genre_id refers to the primary key of the genres table, and a
>>>> genre with genre_id=6 already exists.
>>>>
>>>>
>>>> am i going to have to map an ORM class to each of the composite
>>>> tables, and use references to them via relationship() instead of
>>>> hoping sql can figure it out by itself from the tables that make up
>>>> the join construct?
>>>>
>>>> can supply a sqlite database and some source code if it helps?
>>>
>>> It's typically appropriate to map tables individually and connect them via 
>>> relationship(), if you want to be writing rows to them independently.   
>>> That's what the phrase "partially of new entries in some tables, and 
>>> existing entries in other tables." implies.
>>>
>>>
>>>
>>>>
>>>> rgds,
>>>> Harry
>>>>
>>>> On Thu, Jul 15, 2010 at 9:48 PM, Michael Bayer <mike...@zzzcomputing.com> 
>>>> wrote:
>>>>>
>>>>> On Jul 15, 2010, at 2:52 PM, Harry Percival wrote:
>>>>>
>>>>>> A new problem, which seems to occur in both IronPython and normal Python:
>>>>>>
>>>>>> I have a database with tables for movies, directors, genres (and a
>>>>>> bridging table movie_directors)
>>>>>> I have a class mapped to a join of all three of the above
>>>>>>
>>>>>>>>>
>>>>>> j = join(movies_table,md_table).join(directors_table).join(genres_table)
>>>>>> mapper(MoviesAndDirectorsAndGenres,j.select(use_labels=True).alias('moviesdirectorsgenres'))
>>>>>>
>>>>>> this works fine. i can query the database ok using this mapper.
>>>>>>
>>>>>> The tables already have some values in.
>>>>>> Now, I want to create a new composite object, that references some of
>>>>>> the existing values, but not all - say it's a new movie by an existing
>>>>>> director in an existing genre:
>>>>>>
>>>>>>>>>
>>>>>> new = MoviesAndDirectorsAndGenres()
>>>>>> new.movies_movie_id=8 #does not already exist in the table
>>>>>> new.directors_director_id=2 #already exists in the table
>>>>>> new.genres_genre_id=6 #already exists too
>>>>>> session.add(new)
>>>>>>
>>>>>> this fails with a constraint error / integrity error, because SQLA
>>>>>> tries to re-insert genre #6 even though it already exists. it also
>>>>>> happens if i use session.merge(new).  it also happens if i fully
>>>>>> specify all of the attributes of each movie/director/genre correctly.
>>>>>>
>>>>>> Is this expected behaviour?  is there any way to get sqla to
>>>>>> intelligently only do inserts when necessary?  is it something to do
>>>>>> with cascade configuration, and if so, how do i configure cascades on
>>>>>> a mapper that's not based on relationship() but based on join()?
>>>>>
>>>>> you should be using merge(), and you should ensure that the objects being 
>>>>> merged have the correct primary key values.   If "genre #6" refers to 
>>>>> some other column that isn't the primary key of that row, then you'd have 
>>>>> to ensure you put the correct primary key value on your object first 
>>>>> before merging it.
>>>>>
>>>>> you can always check what decision merge() made by asking, "obj in 
>>>>> session.new" versus "obj in session.dirty".

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to