how to share variables in data migrations (up/down)

20 views
Skip to first unread message

ct9a

unread,
Mar 4, 2010, 5:32:31 AM3/4/10
to Ruby on Rails: Talk
hi there,
How do we share variables in a given data migration?

For example,
the code below fails to work because "statuses" don't seem to be in
scope for both up and down.


class AddDefaultValuesToStatuses < ActiveRecord::Migration
statuses = [
{
'details' => 'details',
},
{
'fitting_profile' => 'fitting profile',
},
{
'live' => 'published on site',
},
{
'sold' => 'auction ended',
},
]


def self.up
statuses.each do |status|
status.each do |name, display_title|
@new_status = Status.new(
:name => name,
:display_title => display_title,
:created_by => 'admin',
:updated_by => 'admin'
).save
end
end
end

def self.down
statuses.each do |status|
status.each do |name, display_title|
@status = Status.searchlogic(
:name => name,
:display_title => display_title
)
@status.delete
end
end
end
end

Andy Jeffries

unread,
Mar 4, 2010, 5:51:57 AM3/4/10
to rubyonra...@googlegroups.com
   How do we share variables in a given data migration?

Personally, in your case I'd declare it as a constant instead of a variable at the class level.
 
class AddDefaultValuesToStatuses < ActiveRecord::Migration
   STATUSES = [

       {
           'details' => 'details',
       },
       {
           'fitting_profile' => 'fitting profile',
       },
       {
           'live'  => 'published on site',
       },
       {
           'sold'  => 'auction ended',
       },
   ]


 def self.up
   STATUSES.each do |status|

     status.each do |name, display_title|
       @new_status = Status.new(
         :name          => name,
         :display_title => display_title,
         :created_by    => 'admin',
         :updated_by    => 'admin'
       ).save
     end
   end
 end

 def self.down
   STATUSES.each do |status|

     status.each do |name, display_title|
       @status = Status.searchlogic(
         :name          => name,
         :display_title => display_title
       )
       @status.delete
     end
   end
 end
end

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To post to this group, send email to rubyonra...@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-ta...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.


Gordon Yeong

unread,
Mar 4, 2010, 6:10:41 AM3/4/10
to rubyonra...@googlegroups.com
good idea but i would still like to learn how to share variables in a given migration.

 Ideas, gents? :) thanks :)

Marnen Laibow-Koser

unread,
Mar 4, 2010, 8:18:42 AM3/4/10
to rubyonra...@googlegroups.com
Gordon Yeong wrote:
> good idea but i would still like to learn how to share variables in a
> given
> migration.
>
> Ideas, gents? :) thanks :)

up and down are class methods. You share variables between them just
like you would between any class methods in Ruby.

However, your original example is problematic. You appear to be using
migrations for seed data. This is a terrible idea. Use seed.rb or
seed-fu instead.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
--
Posted via http://www.ruby-forum.com/.

Gordon Yeong

unread,
Mar 4, 2010, 8:32:52 AM3/4/10
to rubyonra...@googlegroups.com
Marnen,
thanks :) Seed-fu and seed.rb - something for me to read on :D
cheers :)



--

Hassan Schroeder

unread,
Mar 4, 2010, 11:03:34 AM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 5:18 AM, Marnen Laibow-Koser
<li...@ruby-forum.com> wrote:

> However, your original example is problematic. You appear to be using
> migrations for seed data. This is a terrible idea.

Why?

--
Hassan Schroeder ------------------------ hassan.s...@gmail.com
twitter: @hassan

Ivan Nastyukhin

unread,
Mar 4, 2010, 11:31:20 AM3/4/10
to rubyonra...@googlegroups.com
Because migration for definition structure and manage it.for fill data
use seeds , or SQL files.

Sent from my iPhone

On 04.03.2010, at 19:03, Hassan Schroeder <hassan.s...@gmail.com>
wrote:

> --
> You received this message because you are subscribed to the Google
> Groups "Ruby on Rails: Talk" group.

> To post to this group, send email to rubyonrails-
> ta...@googlegroups.com.

Hassan Schroeder

unread,
Mar 4, 2010, 11:50:35 AM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 8:31 AM, Ivan Nastyukhin <diei...@me.com> wrote:
> Because migration for definition structure and manage it.

Just repeating it doesn't make it valid. Again, *why* do you think that?

Michael Pavling

unread,
Mar 4, 2010, 12:14:06 PM3/4/10
to rubyonra...@googlegroups.com
On 4 March 2010 16:50, Hassan Schroeder <hassan.s...@gmail.com> wrote:
> On Thu, Mar 4, 2010 at 8:31 AM, Ivan Nastyukhin <diei...@me.com> wrote:
>> Because migration for definition structure and manage it.
>
> Just repeating it doesn't make it valid. Again, *why* do you think that?
>

Migrations are a "point in time" reference about the structure of a database.

If they alter *data* then they are binding tightly to the models - and
you can no longer have a later migration that adds/removes/renames
columns, because those columns will be set in the data in an earlier
migration.
Similarly, you can now no longer alter your models, because some
migrations rely on the operation of the model at that "point in time"
- if you create a model, with all its validations, field
specifications, etc, and a migration uses that model. If you later try
to change the model by adding or changing a validation, your migration
will no longer work, as it is tightly bound to the old model.

Is that bad enough for you?

Being "decoupled" (separating things from each other, so that they're
all free to change without breaking other things) is the fundamental
principle of MVC - models do their thing, as do views, as do
controllers - and it's made *deliberately* awkward to mix them up...
because it's *bad*. So keep your migrations away from seed data, and
also separate your tests from fixtures.

Of course this assumes that you're not going back and editing
migrations... but you wouldn't do that, because that's bad too! :-)

Rob Biedenharn

unread,
Mar 4, 2010, 12:32:27 PM3/4/10
to rubyonra...@googlegroups.com

On Mar 4, 2010, at 12:14 PM, Michael Pavling wrote:

> On 4 March 2010 16:50, Hassan Schroeder <hassan.s...@gmail.com>
> wrote:
>> On Thu, Mar 4, 2010 at 8:31 AM, Ivan Nastyukhin <diei...@me.com>
>> wrote:
>>> Because migration for definition structure and manage it.
>>
>> Just repeating it doesn't make it valid. Again, *why* do you think
>> that?
>>
>
> Migrations are a "point in time" reference about the structure of a
> database.
>
> If they alter *data* then they are binding tightly to the models - and
> you can no longer have a later migration that adds/removes/renames
> columns, because those columns will be set in the data in an earlier
> migration.
> Similarly, you can now no longer alter your models, because some
> migrations rely on the operation of the model at that "point in time"
> - if you create a model, with all its validations, field
> specifications, etc, and a migration uses that model. If you later try
> to change the model by adding or changing a validation, your migration
> will no longer work, as it is tightly bound to the old model.
>
> Is that bad enough for you?

So, the solution is to define a point-in-time version of your Model
that is just enough for you to use to accomplish the migration (incl.
seeding data).

For example, if you are only working with the new columns of a table,
you can (and I'll argue you SHOULD!) define an empty class inside the
migration.

class CreateGames < ActiveRecord::Migration
class Game < ActiveRecord::Base
end
def self.up
create_table :games do |t|
t.string :name
t.integer :prize
t.timestamps
end
Game.reset_column_information
Game.create(:name => "Jackpot", :prize => 1000000)
Game.create(:name => "Instant Win", :prize => 100)
Game.create(:name => "Pick 4", :prize => 5000)
end

def self.down
drop_table :games
end
end

If you have later code (and another migration) that, say, validates
that prizes must be a minimum of 500, your migration can find and fix
existing data, but THIS migration will still work.

This reduces the coupling to the current schema which is what
ActiveRecord is going to reflect upon. If you need some of the
functionality from the model, then it should be duplicated here in the
migration.

-Rob


>
> Being "decoupled" (separating things from each other, so that they're
> all free to change without breaking other things) is the fundamental
> principle of MVC - models do their thing, as do views, as do
> controllers - and it's made *deliberately* awkward to mix them up...
> because it's *bad*. So keep your migrations away from seed data, and
> also separate your tests from fixtures.
>
> Of course this assumes that you're not going back and editing
> migrations... but you wouldn't do that, because that's bad too! :-)
>

> --
> You received this message because you are subscribed to the Google
> Groups "Ruby on Rails: Talk" group.
> To post to this group, send email to rubyonrails-
> ta...@googlegroups.com.
> To unsubscribe from this group, send email to rubyonrails-ta...@googlegroups.com
> .
> For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en
> .
>

Rob Biedenharn http://agileconsultingllc.com
R...@AgileConsultingLLC.com
+1 513-295-4739
Skype: rob.biedenharn


Hassan Schroeder

unread,
Mar 4, 2010, 12:30:17 PM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 9:14 AM, Michael Pavling <pav...@gmail.com> wrote:

> Migrations are a "point in time" reference about the structure of a database.
>
> If they alter *data* then they are binding tightly to the models - and
> you can no longer have a later migration that adds/removes/renames
> columns, because those columns will be set in the data in an earlier
> migration.

Huh? I can't even parse that last part - "columns set in the data in
an earlier migration"?? What does that mean?

I can't see how a db column being populated with data is going to
keep you from using a migration to update/change it, structure- or
content-wise.

> Similarly, you can now no longer alter your models, because some
> migrations rely on the operation of the model at that "point in time"

Likewise, sorry, that makes zero sense to me. I can't alter a model
because it was altered before? What??

Maybe an example would help...

Michael Pavling

unread,
Mar 4, 2010, 12:58:09 PM3/4/10
to rubyonra...@googlegroups.com
On 4 March 2010 17:30, Hassan Schroeder <hassan.s...@gmail.com> wrote:
>> Similarly, you can now no longer alter your models, because some
>> migrations rely on the operation of the model at that "point in time"
>
> Likewise, sorry, that makes zero sense to me. I can't alter a model
> because it was altered before? What??
>
> Maybe an example would help...

Yesterday I created this model and it's migration:

class Post < ActiveRecord::Base
end

class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :summary
t.string :user_id
t.timestamps
end
# default record for Posts
Post.reset_column_information
Post.create(:name => "My First Post", :user_id => 1)
end

def self.down
drop_table :posts
end
end

Today I decide that I need to have an "expired flag" on my posts, and
it's required, so I create a new migration and edit my model:
class Post < ActiveRecord::Base
validates_presence_of :expires
end

class AlterPostsAddExpired < ActiveRecord::Migration
def self.up
add_column :posts, :expires, :datetime
end

def self.down
remove_column :posts, :expires
end
end

But now, when I roll back my migrations and try to roll them forward
again, the "create" breaks because the "point in time" Post record
*didn't* require an "expires" value (or indeed, even have a field for
it!).

As Rob says, the way around this is to duplicate the model code (or at
least the minimum you need to achieve your result) in the migration -
but this is a smelly, non-DRY, and non-testable way of putting data in
the tables (remember where I said it was "deliberately awkward" to do)
- when the alternative is to use a seed or seed-fu (like, 30 seconds
work), I can't believe you're making me go through these hoops to show
you how bad a practice it is to put data in your migrations
:-/

Rob Biedenharn

unread,
Mar 4, 2010, 1:14:26 PM3/4/10
to rubyonra...@googlegroups.com
On Mar 4, 2010, at 12:58 PM, Michael Pavling wrote:

> On 4 March 2010 17:30, Hassan Schroeder <hassan.s...@gmail.com>
> wrote:
>>> Similarly, you can now no longer alter your models, because some
>>> migrations rely on the operation of the model at that "point in
>>> time"
>>
>> Likewise, sorry, that makes zero sense to me. I can't alter a model
>> because it was altered before? What??
>>
>> Maybe an example would help...
>
> Yesterday I created this model and it's migration:
>
> class Post < ActiveRecord::Base
> end
>

take that model...

> class CreatePosts < ActiveRecord::Migration

and put it right here INSIDE the migration class


You can have the same kind of problems in migrations that aren't
trying to seed data, but just do the kind of things to change existing
data that happen in the real world.

I've helped projects that got themselves into trouble with too many
migrations happening between production deployments and the *set* of
migrations can't even be run forward during the deploy.

-Rob

Hassan Schroeder

unread,
Mar 4, 2010, 1:22:28 PM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 9:58 AM, Michael Pavling <pav...@gmail.com> wrote:
>>> Similarly, you can now no longer alter your models, because some
>>> migrations rely on the operation of the model at that "point in time"

> Today I decide that I need to have an "expired flag" on my posts, and
> it's required, so I create a new migration ...

> But now, when I roll back my migrations and try to roll them forward

> again, ...

Well, sorry, why are you "rolling back" at all? Why not just run the
new migration? I can't remember every needing to do a rollback for
other than a single most recent migration (due to some obvious
d'oh! screwup like a typo).

> I can't believe you're making me go through these hoops to show
> you how bad a practice it is to put data in your migrations

This seems to be a lesson in "at some point old migrations will fail if
(when) your models change enough" rather than anything to do with
migrations and data.

Michael Pavling

unread,
Mar 4, 2010, 2:25:34 PM3/4/10
to rubyonra...@googlegroups.com
On 4 March 2010 18:14, Rob Biedenharn <R...@agileconsultingllc.com> wrote:
> take that model...

> and put it right here INSIDE the migration class

Yes, I did agree with you that that's possible. It's just not very
pleasant, or the simplest thing to do.

Michael Pavling

unread,
Mar 4, 2010, 2:28:47 PM3/4/10
to rubyonra...@googlegroups.com
On 4 March 2010 18:22, Hassan Schroeder <hassan.s...@gmail.com> wrote:
> Well, sorry, why are you "rolling back" at all? Why not just run the
> new migration? I can't remember every needing to do a rollback for
> other than a single most recent migration (due to some obvious
> d'oh! screwup like a typo).

So why are you using migrations at all? And why have data in them?!

I work in a team with other developers, on code that gets deployed
onto test, staging and live servers - migrations get run all the time.
Of course, on my "bedroom" projects where I'm the only person doing
anything on the code, I almost never re-run migrations.

>>  I can't believe you're making me go through these hoops to show
>> you how bad a practice it is to put data in your migrations
>
> This seems to be a lesson in "at some point old migrations will fail if
> (when) your models change enough" rather than anything to do with
> migrations and data.

No - old migrations only fail if you access your models from them.

You asked for an example... you got one. If you choose to ignore the
advice (or you think it doesn't apply to you) that's okay - it's only
advice; a convention; a recommendation. Not a command :-) That's the
beauty of Rails; convention over configuration. Stick to the
conventions and you make everyone's life easier (including your
own)... but hey ho, it's your choice.

Hassan Schroeder

unread,
Mar 4, 2010, 2:51:00 PM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 11:28 AM, Michael Pavling <pav...@gmail.com> wrote:

> I work in a team with other developers, on code that gets deployed
> onto test, staging and live servers - migrations get run all the time.

So do I -- but migrating down multiple levels and back up has never
entered the picture. What circumstances require that?

> You asked for an example... you got one. If you choose to ignore the
> advice (or you think it doesn't apply to you) that's okay - it's only
> advice; a convention; a recommendation.

And yet I still fail to see the evidence supporting this as a "convention",
especially given that the API includes examples of using migrations to
*change data*, *not* just structure.

<http://api.rubyonrails.org/classes/ActiveRecord/Migration.html>

Hence my question :-)

Aldric Giacomoni

unread,
Mar 4, 2010, 3:00:10 PM3/4/10
to rubyonra...@googlegroups.com
Hassan Schroeder wrote:
>
> And yet I still fail to see the evidence supporting this as a
> "convention",
> especially given that the API includes examples of using migrations to
> *change data*, *not* just structure.
>
> http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
>

Well... Alright - one migration creates an index, and another one
removes empty tags. This is a one-time change to the data, which would
be necessary as a migration but would not show up in the schema.rb file.
The original comment was "Don't seed with migrations - seed with the
seeding process". This is still a good idea. :)

Hassan Schroeder

unread,
Mar 4, 2010, 3:17:08 PM3/4/10
to rubyonra...@googlegroups.com
On Thu, Mar 4, 2010 at 12:00 PM, Aldric Giacomoni <li...@ruby-forum.com> wrote:

> The original comment was "Don't seed with migrations - seed with the
> seeding process". This is still a good idea. :)

Just because. Sweet ${DEITY} I give up.

Thanks anyway.

Gordon Yeong

unread,
Mar 4, 2010, 5:53:41 PM3/4/10
to rubyonra...@googlegroups.com
well, in one of my other migrations (which was large), i populated FACT data with raw sqls. That worked really well but this time round, I wanted to use ruby all the way.

Charles A. Lopez

unread,
Mar 4, 2010, 6:28:45 PM3/4/10
to rubyonra...@googlegroups.com
On 4 March 2010 05:51, Andy Jeffries <andyje...@gmail.com> wrote:
   How do we share variables in a given data migration?

Personally, in your case I'd declare it as a constant instead of a variable at the class level.
 

If it were a constant you'd never be able to change the value.  Is that truly intended?



--
Charles A. Lopez
charle...@gmail.com

What's your vision for your organization?
What's your biggest challenge?

Let's talk.
(IBM Partner)


Gordon Yeong

unread,
Mar 4, 2010, 6:29:45 PM3/4/10
to rubyonra...@googlegroups.com
Definitely not.

Charles A. Lopez

unread,
Mar 4, 2010, 6:33:11 PM3/4/10
to rubyonra...@googlegroups.com
To the OP

in plain english, what are you trying to do?

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To post to this group, send email to rubyonra...@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-ta...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

Gordon Yeong

unread,
Mar 4, 2010, 6:36:43 PM3/4/10
to rubyonra...@googlegroups.com
I want to add default fact data to a given database table.

Assuming an online t-shirt sales application, each order catalogue would have the following statuses throughout the life of the application.
  •            'details'
  •            'fitting_profile'
  •            'live on site'
  •            'sold out'
thank you.

Michael Pavling

unread,
Mar 5, 2010, 3:48:17 AM3/5/10
to rubyonra...@googlegroups.com
On 4 March 2010 23:36, Gordon Yeong <anex...@gmail.com> wrote:
> Assuming an online t-shirt sales application, each order catalogue would
> have the following statuses throughout the life of the application.

It would be worth your time to look at using a state machine then.
Either roll your own (it's a fairly simple pattern), or install AASM
(Acts As State Machine) to get going very quickly.

Michael Pavling

unread,
Mar 5, 2010, 4:09:05 AM3/5/10
to rubyonra...@googlegroups.com
On 4 March 2010 19:51, Hassan Schroeder <hassan.s...@gmail.com> wrote:
> So do I -- but migrating down multiple levels and back up has never
> entered the picture.

Good for you. So you never need to migrate; so you never have
migration troubles. However, those people that do migrate (and not
everyone does *exactly* the same thing as you), might occasion some
problems if they put data access in their migrations. You've asked for
examples, and then dismiss them when they're given rather than
understanding that different situations exist for different people.

> What circumstances require that?

Yesterday I had to work on a project I had never worked on before on
this computer; after cloning from the repository, I had to run
migrations up.

> And yet I still fail to see the evidence supporting this as a "convention",
> especially given that the API includes examples of using migrations to
> *change data*, *not* just structure.

Just because it's possible doesn't mean it's good. There's lots of
things the documentation describes; it tells me all about how I can
use different table names and primary keys for models... but these are
generally *bad* as they risk causing unexpected results as development
goes on, especially if not all the developers that come to the code
during its live realise you've deviated from the conventional
practices (but are there for when you *have* to use them - that pesky
legacy table that needs to be integrated, but can't be refactored).

To me, using models in migrations is the same; and emergency tool;
even the example you linked to shows it as being used to alter
existing data, not seed the DB. If a migration I wrote caused me to
*have* to alter existing data, then I'd do the minimum I had to - as
Rob's example showed - to get it working, but I *wouldn't* use my
normal models, as as stated before, I couldn't guarantee that in six
months time that model would validate correctly for the data in my
migration at that point - or even whether the model existed!

>Just because. Sweet ${DEITY} I give up.

No, not "just because" - not like a parent forbidding behaviour for
the sake of it; but the general acknowledgement that when most people
have to complete a task, they try to do it in a way that makes it
easiest for them, and for the people that come after them. If someone
else chooses to do it differently (either to be awkward; because
they're curious; or even that they don't know better), that's fine, if
a little annoying/frustrating :-)

To be honest, I give up too. Because I answered your queries
patiently, yet you don't seem to acknowledge that there might be
things that work for you that might be seen as bad practice in the
broader community. If you want to continue the discussion, then drop
me a line offlist.

Marnen Laibow-Koser

unread,
Mar 5, 2010, 4:39:31 PM3/5/10
to rubyonra...@googlegroups.com
Hassan Schroeder wrote:
> On Thu, Mar 4, 2010 at 5:18 AM, Marnen Laibow-Koser
> <li...@ruby-forum.com> wrote:
>
>> However, your original example is problematic. You appear to be using
>> migrations for seed data. This is a terrible idea.
>
> Why?

For several reasons. The two most compelling for me are as follows.

* First of all, it's conceptually wrong. Migrations are about the
database *schema*, not the data. Each migration file should contain
whatever is necessary to bring the schema from one consistent state to
the next. Those schema changes may include munging the existing data to
fit the new schema's representation, but they can never include seed
data. Once they include seed data, migrations step outside their proper
role of defining the schema.

* Second, putting seed data in migrations forces you to initialize new
installations by running all your migrations starting from zero. This
is a known bad practice, and I think even members of the Rails core team
recommend against it; in general, it's better to set up a new
installation by running rake db:schema:load, but if your seed data is in
your migrations, you can't do that.

I can think of no good reasons to put seed data in migrations, and many
good reasons not to. Conclusion: don't to it.

>
> --
> Hassan Schroeder ------------------------ hassan.s...@gmail.com
> twitter: @hassan

Best,

Reply all
Reply to author
Forward
0 new messages