Thanks for the tip! After playing with it for a bit, I think `compare_metadata`
might be just enough to satisfy my paranoia for the time being. (And it's a
lot less work!)

Thanks again!
Ian


On Sat, Aug 8, 2015 at 4:12 PM, Mike Bayer <mike...@zzzcomputing.com> wrote:

>
>
> On 8/8/15 3:50 PM, Ian McCullough wrote:
>
>
> So I've continued thinking about this a bit, mainly because trying to keep
> the database schema and object model in sync is effectively a kind of a
> "double entry" system, and I have been burned *so. many. times.* in the
> past by (N>1)-entry systems where the different "truths" get out of sync,
> leading to subtle, but week-ruining bugs.
>
>
> of course, this is something we all share.  I only ask that you consider
> that the Alembic migration files and other similar concepts might be
> thought of differently, as they intended to be an *immutable* truth; that
> is, a particular migration file can never be incorrect, because it
> represents a point in time that has already passed.   But I guess that's
> why you favor the migration files as that source of truth.
>
>
> In the abstract, my goal is to have a single source of truth and to have
> that truth 'flow' through the system (in my case, I'm trying to use the
> alembic version scripts, but I really don't care what form the truth takes
> as long as there's only one of them.)
>
> Michael said:
>
>> However, where it gets thorny is that neither Alembic migrations nor
>> SQLAlchemy metadata are supersets of each other
>
>
> My approach here has been to "change" this by leveraging the ability to
> specify arbitrary, ancillary data in the `info` property of `SchemaItem` to
> store any/all additional information necessary to re-create the models
> (i.e. making alembic migrations a superset of the SQLAlchemy metadata *that
> I need, for my specific purposes*) Then, once I've captured that
> metadata, I push it up into the database (in my case, I'm using Postgres's
> COMMENTs feature, but in other DBs it could just be an arbitrary table),
> which can then be used by another, build-time tool to generate my models.py
> file from the database. Ideally, there would be a way to cut out the
> database so that you could just run the alembic scripts and get out the
> appropriate metadata, but going through the database is an acceptable
> detour for me (especially now that I've wrapped up the necessary fixtures
> to spool up an ephemeral, local Postgres installation.) Considering that
> you could conceivably even ship pickled python object graphs to this kind
> of "sidecar" storage, I suspect that there probably *is* enough
> flexibility to capture all the SQLAlchemy metadata, if you carried this to
> its logical conclusion.
>
> I realize this approach is probably too "restrictive" to be useful in the
> general case, but I figured I'd share my thoughts and hacks anyway.
> Conceptually, I think the best thing, long term, would be for alembic to be
> able to handle both DB schema migration and object model migration, and to
> serve as a single source of truth for systems willing to operate completely
> within alembic's purview. Based on your comments about mine being an
> unusual workflow, I assume many folks won't want to work this way, but for
> those who consider single sources of truth to be critical, I think it could
> be a win.
>
> If anyone is interested in talking more about this, let me know.
>
>
> a technique you might want to consider, which we do in Openstack, is that
> as part of our CI suite we actually run the migrations fully and then do a
> diff of the schema as developed against the metadata.   This function is
> available from Alembic using the compare_metadata function:
> http://alembic.readthedocs.org/en/rel_0_7/api.html#alembic.autogenerate.compare_metadata.
> Basically you want it to return nothing, meaning nothing has changed.
>
> This allows the alembic migrations and the current metadata to remain
> separately, but they are checked for accuracy against each other as part of
> the test suite.   It is of course only as accurate as SQLAlchemy reflection
> goes (so things like check constraints, triggers, etc. that aren't
> currently reflected aren't included, unless you augment the comparison with
> these), but as far as those aspects of the model that make a difference
> from the Python side, e.g. names of tables and columns and datatypes, those
> would all be affirmed.
>
>
>
>
>
> Regards,
> Ian
>
>
>
> On Monday, August 3, 2015 at 9:36:19 AM UTC-4, Michael Bayer wrote:
>>
>>
>>
>> On 8/1/15 6:59 PM, Ian McCullough wrote:
>>
>> I've been getting up to speed with SQLAlchemy and alembic and having a
>> great time of it. This is some great stuff!
>>
>> One thing that's been confounding me is this: My Alembic schema revisions
>> are 'authoritative' for my metadata (i.e. I've started from scratch using
>> alembic to build the schema from nothing), yet it doesn't appear that the
>> metadata that exists in my alembic scripts can be leveraged by my models in
>> my main app. So far, I've been maintaining effectively two versions of the
>> metadata, one in the form of the "flattened projection" of my alembic
>> schema rev scripts, and another in my application models scripts. I
>> understand that there are some facilities to auto-re-generate the metadata
>> from the RDBMS on the application side, but that seems potentially "lossy",
>> or at least subject to the whims of whatever DBAPI provider I'm using.
>>
>> Is there a way to pull this flattened projection of metadata out of
>> alembic and into my app's models at runtime? (i.e. import alembic, read the
>> version from the live DB, then build the metadata by playing the upgrade
>> scripts forward, not against the database, but against a metadata
>> instance?) It seems like a fool's errand to try to keep my app models in
>> sync with the flattened projection of the schema revisions by hand. My
>> assumption is that I'm missing something super-obvious here.
>>
>>
>> There's a lot to say on this issue.    The idea of the migrations
>> themselves driving the metadata would be nice, and I think that the recent
>> rewrite of django south does something somewhat analogous to this.
>>
>> Also, the reorganization of Alembic operations into objects that you can
>> hang any number of operations upon, this is due for Alembic 0.8, is also
>> something that we'd leverage to make this kind of thing happen.
>>
>> However, where it gets thorny is that neither Alembic migrations nor
>> SQLAlchemy metadata are supersets of each other.     That is, there's many
>> things in SQLAlchemy metadata that currently has no formal representation
>> in Alembic operations, the primary example is that of Python-side default
>> operations on columns, which have no relevance to emitting ALTER
>> statements.    On the Alembic side, a set of migrations that takes care to
>> only use the official Alembic op.* operations, and also does not use
>> "execute()" for any of them, is the only way to guarantee that each change
>> is potentially representable in SQLAlchemy metadata.    A migration that
>> emits op.execute("ALTER TABLE foo ADD COLUMN xyz") wouldn't work here, and
>> a migration that has lots of conditionals and runtime logic might also not
>> be useful in this way.
>>
>> SQLAlchemy Table and Column objects also do not support removal from
>> their parents.   This would be necessary in order to represent "drop"
>> mutations as targeted at a SQLAlchemy metadata structure.  This is
>> something that could be implemented but SQLA has always made a point to not
>> get into this because it's very complicated to handle "cascades" of
>> dependent objects, whether that means raising an error or mimicking other
>> functionality of a real "drop" operation.
>>
>> Finally, the whole workflow of Alembic up til now has been organized for
>> the opposite workflow; the MetaData is the authoritative model, and
>> migrations are generated using tools like autogenerate to minimize how much
>> they need to be coded by hand (and there is of course no issue of
>> maintaining the same code in two places because migration scripts are a
>> fixed point in time once created).    This model is practical for many
>> reasons; all of the above reasons, plus that it is compatible with
>> applications that weren't using migrations up to point or were using some
>> other system, plus that it allows easy pruning of old migrations.
>>
>>
>>
>>
>>
>>
>> Thanks,
>> Ian
>>
>>
>>
>> --
> 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.
>
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "sqlalchemy-alembic" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/sqlalchemy-alembic/-c02w37LbtM/unsubscribe
> .
> To unsubscribe from this group and all its topics, send an email to
> sqlalchemy-alembic+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

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

Reply via email to