Hello -

I'm learning SA at the moment (using 0.4), so I apologize if any
questions go against the grain or assume something silly, I'm looking
for the "SA answer" to how to do the following things. Similarly, I'm
going to throw some possible syntax up, but I don't know the SA style
well enough yet to know if I'm choosing a good fit, so if there's
isn't a pre-existing implementation that it turns out I missed, I'll
happily code up additions with others' input on syntax.

Ok, my learning task (and a real goal of using SA) is to benefit from
the mapper by being able to swap out actual table definitions and
selects as necessary, just changing the mappers and continuing to use
the mapped objects unchanged. I think that's pretty in-line with what
SA is trying to make possible? My test scenario is two sets of data
(draft/preview and live), with two equivalent models (one table with a
boolean flag, or two tables). Here's some stubs to give some context:

class Data(object):
  ...
class LiveData(object):
  ...
class PreviewData(object):
  ...

# single table:

data_table = Table('data', ..., Column('field1', String,
primary_key=True), Column('field2', String), Column('is_live',
Boolean, nullable=False))

mapper(Data, data_table)
mapper(LiveData, select([data_table.c.field1,
data_table.c.field2]).where(data_table.c.is_live==True).alias('live_data'))
mapper(PreviewData, select([data_table.c.field1,
data_table.c.field2]).where(data_table.c.is_live==False).alias('preview_data'))

# OR separate tables:

live_table = Table('live_data', ..., Column('field1', String,
primary_key=True), Column(field2', String))
preview_table = Table('preview_data', ..., Column('field1', String,
primary_key=True), Column(field2', String))

mapper(LiveData, live_table)
mapper(PreviewData, preview_table)
mapper(Data, select([preview_table, literal_column('0',
Boolean).label('is_live')]).union(select([live_data,
literal_column('1', Boolean).label('is_live')]).alias('data')

This has worked really well up to a point, trying to use the UOW/
session concept and only referring to the mapped objects not the
tables/queries, here are the areas where I'm failing:


1. Inserting into mapped selects.

SA seems to be pretty smart here and makes a decent attempt (according
to the docs, inserts into any table whose primary key is involved in
the select, appears to be working fine). However, my simple example
points out two (possibly generic) reasons to need to go beyond that:

 a. Further conditions on inserting. In the separate table model, the
mapped select is to a union and based on the constructed column
"is_live" I need the insert to go to one of the base tables. Today,
because both base tables contribute their PKs, the insert of creating
a Data object always goes to both.
 b. Implied/constructed values not present in the source objects. In
the single table model, an insert of a LiveData object should add an
is_live=True to the mapped insertion into Data. Today, with
nullable=False on data_table.is_live an insert to LiveData fails with
IntegrityError, with nullable=True is_live is set to NULL.

A naive attempt at syntax for defining these, an "insert_conversion"
argument to mapper of a dictionary of functions keyed by table.
functions would be (source_values_dict) =>
(insert_values_dict_for_this_table) or None. The conditional case is
handled by returning None, open questions would be is the
source_values for all tables or just the table's columns, and whether
arbitrary tables can be given a function in this dictionary or only
those with PKs in the mapped select.

mapper(LiveData, live_query, insert_conversion={data_table: lambda v:
dict(v, is_live=True)})
...
mapper(Data, data_query, insert_conversion={live_table: lambda v: v if
v['is_live'] else None, prevew_table: lambda v: v if not v['is_live']
else None})

(As we go into the second issue, keep in mind that the above problems
for inserts also apply to updates and deletes - I'm not sure whether
the same functions can apply across i/u/d however, or if you'd need to
specify separately. This example scenario can use the same ones, with
some restrictions: funcs cannot assume any column values are included
for update, which does mean updates would still have to be stopped for
ambiguity, and only the boolean return of the function matters for
delete.)

2. Updating/deleting against querysets using the mapped objects

Some of my usecases for this example include making bulk updates/
deletes against sets of data, for instance "make all preview data
live". The combination I'm trying to reach here is both:

  a. A set operation, not iterative.
  b. Using the mapped objects, ie PreviewData or Data.

(Note that (a) probably conflicts with 1. above as specified, although
not at a syntax level.)

A whole series of things that don't work:

# Selects can't be converted to updates
session.query(Data).filter_by(is_live=False).update(is_live=True) #
OR .update().values(dict(is_live=True))
# session.update is entirely different...
session.update(Data, ..., values=dict(is_live=False))
# ...and I can't construct the restriction without referring to
objects on the wrong side of the mapper anyways
session.execute(update(Data, ...))
# ... and update objects don't have filter_by and Data objects don't
have .update

So if I give up criteria b. above (a sizeable blow), this works:

session.execute(data_table.update(data_table.c.is_live==False,
values=dict(is_live=True)))

However, if I say "ok, the free-to-change-schema primary goal just
means I have to leave the table object names alone - if data is no
longer a base table but a unioned select, i'll just still call it
data_table", then the above code stops working (Alias objects have no
attribute 'update'), and even this fails:

# data_query name still used for clarity
session.execute(update(data_query, data_query.c.is_live==False,
values=dict(is_live=True)))

because it still tries to update the "data" table (the alias name)
rather than deal with the select (unlike the attempted intelligent
update that occurs with session.save()/flush() as in 1. above).

-------

Ok, enough for tonight. Am I way off base? It seems that many/all of
the problems for 1. must be handled internally in some fashion for the
polymorphic behaviors of mapper, but I haven't found my way into that
code yet - does it seem sensible/possible to try to generalize that to
relationships other than subclass/superclass, or to db models that
don't have a string type column? For the iterative problem and the
syntax, perhaps the conversion dict's values could be sql expressions,
although they'd pretty much have to be selects although context is
unclear and now inserts would be different from update/delete. For 2,
another consideration that might have kept this off the table so far
would be managing session/uow state when updating objects that may or
may not be loaded yet.

Appreciate any discussion that comes out of this,

- Luke


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

Reply via email to