Would be nice if Alembic exposed friendly Python interfaces

2014-06-10 Thread Jon Rosebaugh
At my workplace, we use Alembic to handle migrations, but we
frequently have minor issues like forgetting to downgrade to the most
recent common migration before switching git branches, or not noticing
that a branch has migrations to be run when checking it out. This
causes a bit of aggravation.

I've just written a post-checkout git hook which helps with this by
inspecting the Alembic revision history and determining if the current
revision is present, and if so does it have any child revisions.
Unfortunately, I'm not very proud of the code I had to write to do
this.

Here's the relevant bits; the full git hook is at
https://gist.github.com/inklesspen/3289015398d14b740074

class CaptureCurrentContext(EnvironmentContext):
# This is a sham EnvironmentContext which only captures the current revision
def __init__(self, cfg, script, **kw):
super(CaptureCurrentContext, self).__init__(cfg, script, **kw)
# we use a set because we have multiple DBs configured in env.py,
# so run_migrations will be called once for each DB.
self.current_revisions = set()

def run_migrations(self, **kw):
self.current_revisions.add(self.get_context().get_current_revision())

with py.path.local(alembic_root).as_cwd():
# as_cwd is a context manager for the current directory
# since the alembic script_location seems to be interpreted relative to cwd,
# rather than the alembic.ini location
fake_cmd_opts = type('args', (object,), {'x': []})()
# Would be nice if I could pass None for cmd_opts, but that causes
a traceback.
cfg = Config(file_=alembic.ini, cmd_opts=fake_cmd_opts)
script = ScriptDirectory.from_config(cfg)

sham = CaptureCurrentContext(cfg, script)
with sham:
script.run_env()

# now we check that both DBs are on the same revision using
sham.current_revisions
# and use script's .get_heads() and .walk_revisions() methods to
get info about the tree

Annoyances:

* have to make a fake cmd_opts

* have to change working directory instead of being able to infer the
script directory from the ini file or an argument

* Have to make a sham EnvironmentContext and actually run the env.py
script because Alembic's env setup relies on module-level code to run
the migration instead of calling a main() function in env.py

* ScriptDirectory.get_revision() raises a util.CommandError -- the
same exception raised by nearly every error case -- instead of
something appropriate to a missing key; also doesn't have a
.has_revision(), so my code has to implement that check with a
try/catch

* In general, I have to get my hands dirty with alembic implementation
details instead of calling cfg.revision_tree() or
cfg.current_revision(). Alembic has commands to get this information,
but they print to stdout instead of returning useful python objects.
IMO it would be better for Alembic to have one layer which produces
the Python objects and then a wrapper layer to print those to stdout.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy-alembic group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy-alembic+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Emission of Different DDL for Null and Not Null Columns

2014-06-10 Thread Jonathan Vanasco


On Monday, June 9, 2014 8:06:52 PM UTC-4, Michael Weylandt wrote:

 You and SQLAlchemy truly are one of the most impressive projects I use on 
 a daily basis. 


+1 

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


[sqlalchemy] another quick question regarding abstract classes

2014-06-10 Thread Richard Gerd Kuesters
so, here i am again with another weird question, but it may be 
interesting for what it may come (i dunno yet).


the problem: i have a collection of abstract classes that, when 
requested, the function (that does the request) checks in a internal 
dictionary if that class was already created or creates it by using 
declarative_base(cls=MyAbstractClass), that later can have an engine 
and then work against a database.


i use this format because i work with multiple backends from multiple 
sources, so abstract classes are a *must* here. now, the problem: 
foreign keys and relationships. it's driving me nuts.


ok, let's say I have 2 classes, Foo and Bar, where Bar have one FK to Foo.


   class Foo(object):
__abstract__ = True
foo_id = Column(...)
...

   class Bar(object):
__abstract__ = True
foo_id = Column(ForeignKey(...))




/(those classes are just examples and weren't further coded because it's 
a conceptual question)/


i know that the code might be wrong, because i can use @declared_attr 
here and furthermore help sqlalchemy act accordingly (i don't know if 
this is the right way to say it in english, but it is not a complain 
about sqlalchemy actions).


ok, suppose I created two subclasses, one from each abstract model (Foo 
and Bar) in a postgres database with some named schema, let's say sc1. 
we then have sc1.foo and sc1.bar.


now, i want to create a third table, also from Bar, but in the sc2 
schema, where its foreign key will reference sc1.foo, which postgres 
supports nicely.


how can i work this out, in a pythonic and sqlalchemy friendly way? does 
@declared_attr solves this? or do I have to map that foreign key (and 
furthermore relationships) in the class mapper, before using it, like a 
@classmethod of some kind?



best regards and sorry for my english,
richard.

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


[sqlalchemy] Sub-classing declarative classes

2014-06-10 Thread Noah Davis
Hi,
   I've been banging my head against this one for several days now, and 
aside from a three year-old post here, I've come up empty.

I've got a python module that defines a set of Declarative models that 
several other applications may use. What I'd like is some way to for the 
individual applications to sub-class the existing Declarative objects, 
without adding any new SQL functionality. Specifically, I'd just like to 
add application-specific helper code to the objects. As an example.

some_model.py
---
[SQLA setup of Base class here]
class Alice(Base):
   __tablename__ = 'alice'
   id = Column(Integer, primary_key=True)
   value = Column(String)

class Bob(Base):
   __tablename__ = 'bob'
   id = Column(Integer, primary_key=True)
   subval = Column(String)
   alice_id = Colum(Integer, ForeignKey('alice.id'))
   alice = relationship('Alice', backref='bobs')


some_app.py

import some_model

class MyAlice(some_model.Alice):
   def myfunc(self):
   do_nothing_sql_related_here()

class MyBob(some_model.Bob):
   def otherfunc(self):
  again_something_unrelated()
-

This actually works okay out of the box if I select on the subclasses:
DBSession.query(MyAlice).filter(MyAlice.id==5).first() - MyAlice(...)

The problem, of course, is relations:
a = DBSession.query(MyAlice).filter(MyAlice.id=1).first()
a.bobs - [Bob(...), Bob(...), Bob(...)]
instead of
a.bobs - [MyBob(...), MyBob(...), MyBob(...)]

I suspect there's some way to tell the ORM to Do The Right Thing here, but 
I have no idea what it might be. I'd like the particular applications to be 
as unaware of the underlying table information as possible. I guess in 
essence I'm trying to separate business logic from the DB logic as much as 
possible. Maybe I'm heading down a dead-end... I'm open to better 
suggestions.

Thanks,
   Noah

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


Re: [sqlalchemy] Sub-classing declarative classes

2014-06-10 Thread Mike Bayer



On Tue Jun 10 15:47:00 2014, Noah Davis wrote:


some_model.py
---
[SQLA setup of Base class here]
class Alice(Base):
   __tablename__ = 'alice'
   id = Column(Integer, primary_key=True)
   value = Column(String)

class Bob(Base):
   __tablename__ = 'bob'
   id = Column(Integer, primary_key=True)
   subval = Column(String)
   alice_id = Colum(Integer, ForeignKey('alice.id'))
   alice = relationship('Alice', backref='bobs')


some_app.py

import some_model

class MyAlice(some_model.Alice):
   def myfunc(self):
   do_nothing_sql_related_here()

class MyBob(some_model.Bob):
   def otherfunc(self):
  again_something_unrelated()
-

This actually works okay out of the box if I select on the subclasses:
DBSession.query(MyAlice).filter(MyAlice.id==5).first() - MyAlice(...)

The problem, of course, is relations:
a = DBSession.query(MyAlice).filter(MyAlice.id=1).first()
a.bobs - [Bob(...), Bob(...), Bob(...)]
instead of
a.bobs - [MyBob(...), MyBob(...), MyBob(...)]

I suspect there's some way to tell the ORM to Do The Right Thing here,


Well IMHO it is doing the Right Thing right now, Alice has a 
relationship that points to Bob.  So it's going to give you Bob 
objects.   I don't know how any system could be devised such that it 
would know you want to go to MyBob instead.  Especially if you have 
MyBobOne, MyBobTwo, etc.


if you wanted MyBob you'd need to tell it that.   SQLA isn't really 
expecting this kind of thing but you can make it work, with warnings, 
like this:


from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Alice(Base):
   __tablename__ = 'alice'
   id = Column(Integer, primary_key=True)
   bobs = relationship(Bob, back_populates=alice)

class Bob(Base):
   __tablename__ = 'bob'
   id = Column(Integer, primary_key=True)
   alice_id = Column(Integer, ForeignKey('alice.id'))
   alice = relationship('Alice', back_populates=bobs)


class MyAlice(Alice):
   def myfunc(self):
  print myfunc

   bobs = relationship(MyBob, back_populates=alice)

class MyBob(Bob):
   def otherfunc(self):
   print otherfunc

   alice = relationship('MyAlice', back_populates=bobs)

e = create_engine(sqlite://, echo=True)
Base.metadata.create_all(e)

s = Session(e)
s.add_all([
   MyAlice(
   bobs = [
   MyBob(),
   MyBob()
   ]
   )
])
s.commit()
s.close()

a1 = s.query(MyAlice).first()
print a1.bobs

this isn't really a great approach though.


but I have no idea what it might be. I'd like the particular
applications to be as unaware of the underlying table information as
possible. I guess in essence I'm trying to separate business logic
from the DB logic as much as possible. Maybe I'm heading down a
dead-end... I'm open to better suggestions.


well if your really want it that way, you can define Table objects 
separately.  Relationships you can get in there using declarative mixin 
patterns perhaps.But if you want a custom-defined MyAlice to point 
to a custom-defined MyBob you'd need some system that knows how to 
figure that out.


Here's a goofy way to do it by name, you might want to get into 
something more comprehensive but I'm hoping this is inspiration...


from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr

class DynamicHierarchy(object):

   @classmethod
   def hierarchy(cls, name):
   target_hierarchy = cls.hierarchy_name
   return cls._decl_class_registry[target_hierarchy + name]

Base = declarative_base(cls=DynamicHierarchy)

class Alice(Base):
   __abstract__ = True

   @declared_attr
   def __table__(cls):
   return Table(alice,
   cls.metadata,
   Column('id', Integer, primary_key=True),
   useexisting=True
   )

   @declared_attr
   def bobs(cls):
   return relationship(lambda: cls.hierarchy(Bob), 
back_populates=alice)


class Bob(Base):
   __abstract__ = True

   @declared_attr
   def __table__(cls):
   return Table(bob,
   cls.metadata,
   Column('id', Integer, primary_key=True),
   Column(alice_id, ForeignKey('alice.id')),
   useexisting=True
   )
   @declared_attr
   def alice(cls):
   return relationship(lambda: cls.hierarchy('Alice'), 
back_populates=bobs)



class MyAlice(Alice):
   hierarchy_name = My

   def myfunc(self):
  print myfunc


class MyBob(Bob):
   hierarchy_name = My

   def otherfunc(self):
   print otherfunc


e = create_engine(sqlite://, echo=True)
Base.metadata.create_all(e)

s = Session(e)
s.add_all([
   MyAlice(
   bobs = [
   MyBob(),
   MyBob()
   ]
   )
])
s.commit()
s.close()

a1 = s.query(MyAlice).first()

Re: [sqlalchemy] another quick question regarding abstract classes

2014-06-10 Thread Mike Bayer

On Tue Jun 10 15:36:09 2014, Richard Gerd Kuesters wrote:

so, here i am again with another weird question, but it may be
interesting for what it may come (i dunno yet).

the problem: i have a collection of abstract classes that, when
requested, the function (that does the request) checks in a internal
dictionary if that class was already created or creates it by using
declarative_base(cls=MyAbstractClass), that later can have an engine
and then work against a database.

i use this format because i work with multiple backends from multiple
sources, so abstract classes are a *must* here. now, the problem:
foreign keys and relationships. it's driving me nuts.

ok, let's say I have 2 classes, Foo and Bar, where Bar have one FK to Foo.


class Foo(object):
__abstract__ = True
foo_id = Column(...)
...

class Bar(object):
__abstract__ = True
foo_id = Column(ForeignKey(...))




/(those classes are just examples and weren't further coded because
it's a conceptual question)/

i know that the code might be wrong, because i can use @declared_attr
here and furthermore help sqlalchemy act accordingly (i don't know if
this is the right way to say it in english, but it is not a complain
about sqlalchemy actions).

ok, suppose I created two subclasses, one from each abstract model
(Foo and Bar) in a postgres database with some named schema, let's say
sc1. we then have sc1.foo and sc1.bar.

now, i want to create a third table, also from Bar, but in the sc2
schema, where its foreign key will reference sc1.foo, which postgres
supports nicely.

how can i work this out, in a pythonic and sqlalchemy friendly way?
does @declared_attr solves this? or do I have to map that foreign key
(and furthermore relationships) in the class mapper, before using it,
like a @classmethod of some kind?



@declared_attr can help since the decorated function is called with 
cls as an argument.  You can look on cls for __table_args__ or some 
other attribute if you need, and you can create a Table on the fly to 
serve as secondary, see 
http://docs.sqlalchemy.org/en/rel_0_9/_modules/examples/generic_associations/table_per_related.html 
for an example of what this looks like.







best regards and sorry for my english,
richard.

--
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
mailto:sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com
mailto:sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


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


[sqlalchemy] Re: Sub-classing declarative classes

2014-06-10 Thread Noah Davis
Sorry, poor choice of words on my part, there. I meant do the Right Thing 
by the model I was trying to construct. Both of these examples are exactly 
what I was looking for - something to point me in the right direction.  I'm 
also not convinced my model is the best solution to my problem, but at 
least now I can play around with it and see what breaks. Thanks for the 
tips, this helps immensely.

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