Activeadmin: Install issue regarding migrations

Created on 24 Nov 2011  Â·  61Comments  Â·  Source: activeadmin/activeadmin

Just ran across something which caused me a bit of a headache; finally figured out what it was!
Installing for the first time I get an exception when generating the assets:

rails g active_admin:install
# ...
    generate  active_admin:assets
Mysql2::Error: Table 'fwd_development.active_admin_comments' doesn't exist: SHOW FIELDS FROM `active_admin_comments` (ActiveRecord::StatementInvalid)
# ...
      create  db/migrate/20111124160741_create_admin_notes.rb
      create  db/migrate/20111124160742_move_admin_notes_to_comments.rb

Then, when running the migrations I got the same error:

rake db:migrate
rake aborted!
Mysql2::Error: Table 'fwd_development.active_admin_comments' doesn't exist: SHOW FIELDS FROM 'active_admin_comments'

Tasks: TOP => db:migrate => environment
(See full trace by running task with --trace)

I was able to get past the migration error by removing the following two lines from the routes.rb file and putting them back afterwards:

ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config

I then successfully generated the assets using rails generate active_admin:assets.

Not sure if anyone else has experienced this but I thought I'd report it anyway :)
I'm using the master branch, by the way.

bug

Most helpful comment

Better version:

ActiveAdmin.routes(self) rescue ActiveAdmin::DatabaseHitDuringLoad

All 61 comments

Yeah, I just experienced this too. It seems to break all of the database-related rake tasks.

What version of Active Admin and Rails are you using?

I was using master branch at the time I submitted the issue.

I commented the line in the routes.rb file and it worked here.

ActiveAdmin.routes(self)

I'm using the activeadmin 0.3.4 gem with Rails 3.0.11.

Oh and Rake 0.9.2, if that helps.

Same here when deploying to production:

ruby-1.9.3-p0
activeadmin-0.3.4
rails-3.1.3
rake-0.9.2.2

Ditto,

ruby-1.9.2-p290
activeadmin-0.4.0
rails-3.1.3
rake-0.9.2.2

For some reason, ActiveAdmin wants to query all the tables it knows about and raises an error if they don't exist yet. This means that rake db:migrate is useless on new environments. So a first deploy to a production environment, for example, just won't run at all.

This also takes out the rails console, which loads the routes as it loads the environment.

My workaround is to rescue all exceptions around ActiveAdmin in routes -

config/routes.rb:

  begin
    ActiveAdmin.routes(self)
  rescue Exception => e
    puts "ActiveAdmin: #{e.class}: #{e}"
  end

which prints out the error where you can see it, but doesn't halt execution. It might be better, though, if ActiveAdmin handled the error more gracefully.

I was able to reproduce this issue on versions 0.3.4 and 0.4.2, however, once I moved my SASS gem out of the assets group, I no longer received this error in any environments.

Can anyone validate that this isn't the cause? And perhaps post your Gemfile if so.

Here is my Gemfile. I still think that errors in ActiveAdmin shouldn't prevent the environment from loading.

source 'http://rubygems.org'

gem 'rails', '3.1.3'

# Bundle edge Rails instead:
# gem 'rails',     :git => 'git://github.com/rails/rails.git'

gem 'mysql2'
gem 'sorcery'
gem 'simple_form', :git => 'git://github.com/plataformatec/simple_form.git'

# For S3 storage of files -
gem 'aws-s3', :require => 'aws/s3'

# ActiveAdmin for simple admin forms.
# Also requires sass-rails and meta_search, so sass-rails moved out
# of :assets group.
gem 'activeadmin'
gem 'sass-rails',   '~> 3.1.5'
gem 'meta_search',  '>= 1.1.0.pre'


# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'coffee-rails', '~> 3.1.1'
  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'
gem 'tinymce-rails'

group :test do
  # Pretty printed test output
  #gem 'turn', '0.8.2', :require => false
end

group :development do
#  gem 'pry'
#  gem 'pry-rails'
  gem 'mailcatcher'
end

I was getting a similar issue as seen here against all versions to 0.4.2, but when I updated to master, I was no longer able to see this issue.

I've got the workaround in place for now, and the product needs to be code complete this week - so will try updating after the launch to see if that resolves the issue without introducing others.

On Feb 22, 2012, at 10:55 AM, jeff ancel wrote:

I was getting a similar issue as seen here against all versions to 0.4.2, but when I updated to master, I was no longer able to see this issue.


Reply to this email directly or view it on GitHub:
https://github.com/gregbell/active_admin/issues/783#issuecomment-4106752

Update on this ... first install to staging environment is a no-go, and it's this issue in the way. Rails 3.1.3, active-admin-0.4.2.

Gotta say it's pretty annoying to have to edit the routes file to deploy.

Encountered the same issue. If I understand this correctly, the issue is that calling ActiveAdmin.routes(self) loads all models. If those models reference columns that do not yet exist (for example during a migration), it results in errors. @zetlan's fix works in that case.

This issue is still present in active-admin 0.4.4.

Just encountered this issue on activeadmin-0.5.0 as well.

Same issue. For completing migration i've commented

#ActiveAdmin.routes(self)

and migration complete fine. Than just comment it out.

I notice that the resource files in /app/admin reference the raw classes instead of a string with the given class... could we not delay the initialization until later? why do we need to actually read the models to write the routes file? it seems like we could simply call ActiveAdmin.register "ClassNameNotTheActualClass" and constantize it later.

tracing through the code it seems like you'd just need to redefine Resource to accept a String or a Class.

if we did this and simply initialized @resource_class_name, would the classes ever need to be constantized for routing to complete?

editing the files i mentioned above gets me further along, but things still crap out when we try to actually store the resource... it wants to create an ActiveAdmin::Resource::Name, which wants to check the class for things like 'model_name' and pass it along to ActiveModel::Name#initialize, why do we need this? wouldn't using a stupid string like "ClassName" as the hash key work just as well?

here's a stacktrace i'm getting... I've put an exception in listing.rb, to trace where it is being initialized from:

/home/fringd/code/active_admin/lib/active_admin/namespace.rb:169:in `eval': uncaught throw "NOOOOOOO" (ArgumentError)
        from /home/fringd/code/swapt_web/app/models/listing.rb:1:in `<top (required)>'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:469:in `load'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:469:in `block in load_file'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:639:in `new_constants_in'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:468:in `load_file'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:353:in `require_or_load'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:502:in `load_missing_constant'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:192:in `block in const_missing'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:190:in `each'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:190:in `const_missing'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:514:in `load_missing_constant'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:192:in `block in const_missing'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:190:in `each'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:190:in `const_missing'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/inflector/methods.rb:230:in `block in constantize'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/inflector/methods.rb:229:in `each'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/inflector/methods.rb:229:in `constantize'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/inflector/methods.rb:260:in `safe_constantize'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/core_ext/string/inflections.rb:66:in `safe_constantize'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_controller/metal/params_wrapper.rb:152:in `_default_wrap_model'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_controller/metal/params_wrapper.rb:169:in `_set_wrapper_defaults'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_controller/metal/params_wrapper.rb:133:in `inherited'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/abstract_controller/railties/routes_helpers.rb:7:in `block (2 levels) in with'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_controller/railties/paths.rb:7:in `block (2 levels) in with'
        from /home/fringd/.rvm/gems/ruby-1.9.3-p194/gems/inherited_resources-1.3.1/lib/inherited_resources/class_methods.rb:369:in `inherited'
        from /home/fringd/code/active_admin/lib/active_admin/application.rb:182:in `block in load!'
        from /home/fringd/code/active_admin/lib/active_admin/application.rb:182:in `each'
        from /home/fringd/code/active_admin/lib/active_admin/application.rb:182:in `load!'
        from /home/fringd/code/active_admin/lib/active_admin/application.rb:205:in `routes'
        from /home/fringd/code/active_admin/lib/active_admin.rb:79:in `routes'
        from /home/fringd/code/swapt_web/config/routes.rb:3:in `block in <top (required)>'

@fringd I don't know ActiveAdmin internals very well but I think your approach to use string for ActiveAdmin.register is the way to go about this issue. I hope you will be able to fix it!

Erm, The issue wasn't fixed yet. Why was this closed ?

It can be solved by catching an exception in your routes.rb file. But if you really want to use it - find exactly which exception you want to catch, because catching general Exception is a big no-no.

The code:

  begin
      ActiveAdmin.routes(self)
  rescue Exception => e
      puts "ActiveAdmin: #{e.class}: #{e}"
  end

Thanks for the work around.

But please track the exact Exception which is thrown and post it here for others as I am unable to do it now.

"Mysql2::Error", but it can be specific to someone's db.

Confirmed in Rails 3.2.12, ActiveAdmin 0.5.1. This needs to be resolved. Note that this issue can also be reproduced simply executing the "environment" task. Here's the pertinent part of the back trace:

 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin/application.rb:190:in `load!'",
 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin/application.rb:190:in `each'",
 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin/application.rb:190:in `load!'",
 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin/application.rb:213:in `routes'",
 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin.rb:79:in `__send__'",
 "/vagrant/vendor/gems/ruby/1.8/gems/activeadmin-0.5.1/lib/active_admin.rb:79:in `routes'",

@jurgelenas the exception I'm seeing is ActiveRecord::StatementInvalid

Additional information. Wrapping any scopes in the classes ActiveAdmin complains about resolves the problem. Given that this doesn't happen when ActiveAdmin is uninstalled from the project, it's most certainly a load error problem with ActiveAdmin. We need class loading deferred.

I think the root cause of the problem appears to be that ActiveAdmin hooks into the routes, so any time you run a rake task that evaluates routes.rb it calls ActiveAdmin. ActiveAdmin needs to hook into the database, so it starts looking for whatever you've told it to look for, including any columns you've added to your code. Unfortunately those columns haven't been deployed to the database yet (because you haven't run rake db:migrate).

On the one hand, why does rake db:migrate need to evaluate the routes? On the other hand, why is ActiveAdmin hooking in at that level? Defining routes is one thing, but actually executing the code that hooks into the database, just to define the routes … I don't get it. Then again, I don't really know the internals of ActiveAdmin, so I'm sure there's a reason.

I do wish, though, that after a year or more of living with this issue the project maintainers might see fit to address it. Or to find collaborators with more time on their hands.

It seems to work fine as long as you don't find yourself having to name columns in your ActiveAdmin-related code.

On Feb 13, 2013, at 10:36 AM, James Herdman [email protected] wrote:

@jurgelenas the exception I'm seeing is ActiveRecord::StatementInvalid

—
Reply to this email directly or view it on GitHub.

I'm not having this issue. The steps I took:

rails new aa-issue-783
vim Gemfile # add Active Admin
bundle
rails g active_admin:install
rake -T # loads the Rails env; works just fine
rails runner "ActiveAdmin::Comment" # works just fine
rails runner "ActiveAdmin::Comment.new" # raises Could not find table 'active_admin_comments' (ActiveRecord::StatementInvalid)

Now the key here might be MySQL, as many people in this Issue were using it. I just used the default, SQLite.

I think the key is in any customization of the interface. I have a few models where I want to limit the items that show in the admin panel, so I have definitions like:

ActiveAdmin.register Foo do
index do
column :id
column :name
column :excerpt
end
end

Later, if I alter the columns in the Foo table as part of a migration (e.g., add a new column) and add that column into the list (column :newcolumn), I'll get a database error. The solution so far has been to either trap and ignore the exception (inside routes.rb) or to migrate in one release, then alter the admin panel in the next.

I wonder if anyone's tried this against Postgresql? Or Oracle? Or some other relational DB? SQLite is notoriously permissive of errors (i.e., "robust").

On Feb 13, 2013, at 11:30 AM, Sean Ian Linsley [email protected] wrote:

I'm not having this issue. The steps I took:

rails new aa-issue-783
vim Gemfile # add Active Admin
bundle
rake -T # loads the Rails env; works just fine
rails runner "ActiveAdmin::Comment" # works just fine
rails runner "ActiveAdmin::Comment.new" # raises Could not find table 'active_admin_comments' (ActiveRecord::StatementInvalid)
All without migrating the database.

Now the key here might be MySQL, as many people in this Issue were using it. I just used the default, SQLite.

—
Reply to this email directly or view it on GitHub.

Wait wait wait... @zetlan, you're building the UI before migrating the database? Well there's your problem!

The standard flow is:

rails g model Stuff foo:string bar:string
# confirm that the migration file is what you want
rake db:migrate
# THEN add an ActiveAdmin file for that resource

A similar rule applies when you're adding columns to an existing resource; run the migration before adding it to your view.

You mentioned that this might force you to "migrate in one release". By that, I assume you mean one commit. Why aren't you doing that now?

No, you're misunderstanding.

When I go through this standard flow, I get the error:
rails g model Stuff foo:String bar:string
# Now migrate the local DB
rake db:migrate
# Now build the UI and add ActiveAdmin UI as well
#… tag and prep for deploy ….

# Now at production time:
cap deploy
# BOOM! Error. ActiveAdmin has UI defined (this happened during git checkout) but the production database hasn't been updated yet, so the deploy fails at rake db:migrate).

The error never occurs locally, because I always run rake db:migrate _before_ writing new UI code. But when rolling to production, the updated UI, ActiveAdmin UI, and models all roll out together. This puts the new code on the system a few command lines away from where rake db:migrate runs. Amusingly, it's often rake db:migrate that fails.

On Feb 14, 2013, at 11:42 AM, Sean Ian Linsley [email protected] wrote:

Wait wait wait... @zetlan, you're building the UI before migrating the database? Well there's your problem!

The standard flow is:

rails g model Stuff foo:string bar:string

confirm that the migration file is what you want

rake db:migrate

THEN add an ActiveAdmin file for that resource

A similar rule applies when you're adding columns to an existing resource; run the migration before adding it to your view.

You mentioned that this might force you to "migrate in one release". By that, I assume you mean one commit. Why aren't you doing that now?

—
Reply to this email directly or view it on GitHub.

Oh I see what you mean. In that case, this should definitely be fixed. The question now is: what's the best option?

It would be nice if we could delay the DB check (like ActiveRecord) until the first request.

ActiveAdmin should never load until you absolutely need it to, period. It's already a brutal hog for your resources, and adds significantly to our load time.

It's not quite so easy because Active Admin has to examine any app/admin/ files in order to set up controllers and routes. But that doesn't require that the DB even exist.

I gave bug hunting another try, but still couldn't get it to occur. Can someone build a rails app from scratch that throws this error, and upload it to GitHub?

Here's what I did. Using the test rails app I made earlier in this thread, I added a fake model

rails g model Foo a b c

And an AA UI filled with every function I could think of

ActiveAdmin.register Foo do
  menu priority: 9001

  scope :g
  filter :fff

  collection_action :ko do; end
  member_action :gggg do; end
  batch_action :lol do; end

  index do
    selectable_column
    id_column
    columns :a, :b, :idf
    default_actions
  end
  show do
    table_for nil do
      column :k
    end
    active_admin_comments
  end
  form do |f|
    f.inputs do
      f.input :lll
    end
    f.actions
  end
end

Then started up the app (both in development and production mode) before migrating Foo into the DB without problem
EDIT: I just tried this out with PostgreSQL as well

Just catching the exception in the routes file may work for some people but not for me.

Correction: Catching the exception in the routes file does fix the active admin problem for me. I thought otherwise before I discovered that I have the same problem caused by my factory girl factories. :(

@mark100net can you provide steps (for a new rails app) to reproduce this problem? Because I haven't run into it.

@daxter Are you trying with postgres? It seems like many (most?) of the people having this problem are using postgres. In any case I will try to do so when I get a chance.

Where are you getting your information from? Because my reading of this thread says MySQL is more common.

But I've tried both with PostgreSQL and SQLite.

i fixed it by wrapping Resource code in

 if ActiveRecord::Base.connection.table_exists? 'some_table_name'
   ActiveAdmin.register SomeTableName do 

...

Despite my best efforts, I can't reproduce this problem. I'm going to close this Issue until this can be reproduced.

As a closing remark, this is the one possible problem people may be running into:

filter :foo, collection: Bar.where(stuff: true) # breaks if the DB hasn't been migrated
filter :foo, collection: ->{ Bar.where(stuff: true) } # delays lookup, so nothing breaks

There is another way to run into this problem, that I thought I should document here for anyone else that might run into it.

If your model is built to depend on the database at parse-time, like this:

class User < ActiveRecord::Base
  FORM_FIELDS = column_names - %w[id user_id created_at updated_at]
end

You need to rewrite it to lazy-load:

class User < ActiveRecord::Base
  def self.form_fields
    column_names - %w[id user_id created_at updated_at]
  end
end

I went ahead and added an AA-specific exception in aa10436f207d6a90989c417f59dfa13d782a76e9 so it's clear that it's something that the developer has to fix.

I'm still having this problem on 0.6.3. Catching the ActiveAdmin route exception resolved it.

I also think this should be safeguarded by ActiveAdmin and it shouldn't break the database migration task(s), it's a bug, should be reopened.

+1 for reopening. I produced the issue by creating a view-backed model on postgresql and customizing the input form. Using 1.0.0.pre and Rails 4.1 on ruby 2.1.4 in my case.

+1 for reopening. I am seeing a similar issue when attempting to create and migrate a DB after adding a collection to a filter.

Here is the error:

ActiveAdmin::DatabaseHitDuringLoad: Your file, app/admin/image.rb (line 15), caused a database error while Active Admin was loading. This is most common when your database is missing or doesn't have the latest migrations applied. To prevent this error, move the code to a place where it will only be run when a page is rendered. One solution can be, to wrap the query in a Proc.Original error message: PG::UndefinedTable: ERROR:  relation "users" does not exist
LINE 1: SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LI...
                               ^
: SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1000
...

Here is the filter:

filter :user, as: :select, collection: User.order(:last_name, :first_name).find_each.map{|u| ["#{u.full_name} - #{u.email}", u.id]}

If I use a very simple filter filter :user, the migrations run. The filter code works great if added in after the DB is created and migrated.

I am using:

  • Rails 4.2.0
  • ActiveAdmin (gem 'activeadmin', github: 'activeadmin')
  • Postgres 9.3.6

@clok in production, ActiveAdmin classes are loaded before the database is connected. As a workaround, you can pass a lambda :

filter :user, as: :select, collection: -> { User.order(:last_name, :first_name).find_each.map{|u| ["#{u.full_name} - #{u.email}", u.id]} }

me too

The workaround by @developer88 works fine for me, but it doesn't really feel like an ideal solution.

FYI I just ran into this as well, I have a line in my model that gets loaded by ActiveAdmin before db migration and made deploy fail.

self.columns_hash['time_block_start_time'] = ActiveRecord::ConnectionAdapters::Column.new('time_block_start_time', nil, ActiveRecord::Type::Time.new)

Don't think that's something I can solve with a lambda. I'd also love to avoid the @developer88 hack if possible.

ActiveAdmin.routes(self) rescue true

@Nazark It is help me, and i think more 'tricky' than add for each Collection -> {}, in model a lot of scopes
but can you clarify this?

@frontandstart it is just a shorter version of https://github.com/activeadmin/activeadmin/issues/783#issuecomment-4076929 - just INLINE
and doesn't require code changes back and forth like in https://github.com/activeadmin/activeadmin/issues/783#issuecomment-8929622

Better version:

ActiveAdmin.routes(self) rescue ActiveAdmin::DatabaseHitDuringLoad

For me, this was triggered rebuilding the TEST database after creating a module included by many AA-managed modules that calls self.column_names to determine type of table support.

Incidentally, this code itself caused no issues in the DEVELOPMENT environment... extra fun since both dbs are PostGres --running on the same machine!

Just got hit by this again. This should be reopened.

Ok, I eventually found the culprit in my own case. I was defining a scope when a model class was being loaded, and this scope was actually hitting the database (moved to a lambda instead).

That being said, I don't really know if this is a problem on AA's side or not :)

I mean, trying to avoid hitting the db when a model class is loaded makes sense, but AFAIK it's not something that Rails enforces. So maybe AA shouldn't either?

I haven't had a close look, but the error seems accurate: DB is being hit during load. The question is whether that's deadly for AA or if we can/should recover. I don't really have an opinion since I haven't studied the problem, but since quite a few people requested reopening, and I don't really mind this being closed or open, I reopened it :man_shrugging:.

Was this page helpful?
0 / 5 - 0 ratings