Would be nice if Alembic exposed friendly Python interfaces
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
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
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
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
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
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
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.