我想我在acts-as-taggable-on 中发现了一个奇怪的错误。其他人可以重现此错误吗?从一个裸 Rails 3 项目开始:
~ [13:57:32]: cd Development/
~/Development [13:59:44]: rvm use 1.9.3
Using /Users/andywhite/.rvm/gems/ruby-1.9.3-p194
Running /Users/andywhite/.rvm/hooks/after_use
~/Development [13:59:54]: rails -v
Rails 3.2.8
~/Development [14:00:01]: rails new foo --skip-bundle
create
create README.rdoc
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
(snip)
create vendor/assets/javascripts/.gitkeep
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.gitkeep
create vendor/plugins
create vendor/plugins/.gitkeep
~/Development [14:00:21]: cd foo
~/Development/foo [14:01:26]: rvm --rvmrc --create 1.9.3@foo
~/Development/foo [14:02:38]: cd ..
~/Development [14:02:40]: cd -
/Users/andywhite/Development/foo
====================================================================================
= NOTICE =
====================================================================================
= RVM has encountered a new or modified .rvmrc file in the current directory =
= This is a shell script and therefore may contain any shell commands. =
= =
= Examine the contents of this file carefully to be sure the contents are =
= safe before trusting it! ( Choose v[iew] below to view the contents ) =
====================================================================================
Do you wish to trust this .rvmrc file? (/Users/andywhite/Development/foo/.rvmrc)
y[es], n[o], v[iew], c[ancel]> yes
~/Development/foo [14:02:44]: vi Gemfile
~/Development/foo [14:08:18]: cat Gemfile
source 'http://rubygems.org'
gem 'rails', '3.2.8'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', :platforms => :ruby
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
# To use Jbuilder templates for JSON
# gem 'jbuilder'
# Use unicorn as the app server
# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
# To use debugger
# gem 'debugger'
gem 'acts-as-taggable-on'
~/Development/foo [14:08:26]: bundle
Fetching gem metadata from http://rubygems.org/.........
Using rake (0.9.2.2)
Installing i18n (0.6.1)
Installing multi_json (1.3.6)
Installing activesupport (3.2.8)
Installing builder (3.0.3)
Installing activemodel (3.2.8)
Installing erubis (2.7.0)
Installing journey (1.0.4)
Installing rack (1.4.1)
Installing rack-cache (1.2)
Installing rack-test (0.6.2)
Installing hike (1.2.1)
Installing tilt (1.3.3)
Installing sprockets (2.1.3)
Installing actionpack (3.2.8)
Installing mime-types (1.19)
Installing polyglot (0.3.3)
Installing treetop (1.4.10)
Installing mail (2.4.4)
Installing actionmailer (3.2.8)
Installing arel (3.0.2)
Installing tzinfo (0.3.33)
Installing activerecord (3.2.8)
Installing activeresource (3.2.8)
Using bundler (1.2.0)
Installing rack-ssl (1.3.2)
Installing json (1.7.5) with native extensions
Installing rdoc (3.12)
Installing thor (0.16.0)
Installing railties (3.2.8)
Installing rails (3.2.8)
Installing acts-as-taggable-on (2.3.3)
Installing coffee-script-source (1.3.3)
Installing execjs (1.4.0)
Installing coffee-script (2.2.0)
Installing coffee-rails (3.2.2)
Installing jquery-rails (2.1.3)
Installing sass (3.2.1)
Installing sass-rails (3.2.5)
Installing sqlite3 (1.3.6) with native extensions
Installing uglifier (1.3.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
Post-install message from rdoc:
Depending on your version of ruby, you may need to install ruby rdoc/ri data:
<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
~/Development/foo [14:08:58]: rails g model Post body
invoke active_record
create db/migrate/20120930130941_create_posts.rb
create app/models/post.rb
invoke test_unit
create test/unit/post_test.rb
create test/fixtures/posts.yml
~/Development/foo [14:09:41]: rails g acts_as_taggable_on:migration
create db/migrate/20120930131032_acts_as_taggable_on_migration.rb
~/Development/foo [14:10:31]: rake db:migrate
== CreatePosts: migrating ====================================================
-- create_table(:posts)
-> 0.0014s
== CreatePosts: migrated (0.0015s) ===========================================
== ActsAsTaggableOnMigration: migrating ======================================
-- create_table(:tags)
-> 0.0013s
-- create_table(:taggings)
-> 0.0012s
-- add_index(:taggings, :tag_id)
-> 0.0005s
-- add_index(:taggings, [:taggable_id, :taggable_type, :context])
-> 0.0007s
== ActsAsTaggableOnMigration: migrated (0.0043s) =============================
~/Development/foo [14:10:41]: vi app/models/post.rb
~/Development/foo [14:11:32]: cat app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :body, :tag_list
acts_as_taggable
end
~/Development/foo [14:11:42]: rails c
Loading development environment (Rails 3.2.8)
1.9.3-p194 :001 > p = Post.create :body => 'My hat blew off'
(0.1ms) begin transaction
SQL (217.4ms) INSERT INTO "posts" ("body", "created_at", "updated_at") VALUES (?, ?, ?) [["body", "My hat blew off"], ["created_at", Sun, 30 Sep 2012 13:12:28 UTC +00:00], ["updated_at", Sun, 30 Sep 2012 13:12:28 UTC +00:00]]
(2.8ms) commit transaction
=> #<Post id: 1, body: "My hat blew off", created_at: "2012-09-30 13:12:28", updated_at: "2012-09-30 13:12:28">
1.9.3-p194 :002 > p.tag_list = "news, boring, hats"
ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Post' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
=> "news, boring, hats"
1.9.3-p194 :003 > p.save
(0.1ms) begin transaction
(0.6ms) UPDATE "posts" SET "updated_at" = '2012-09-30 13:13:28.210803' WHERE "posts"."id" = 1
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" WHERE (lower(name) = 'news' OR lower(name) = 'boring' OR lower(name) = 'hats')
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'news' LIMIT 1
SQL (0.2ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "news"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'boring' LIMIT 1
SQL (0.0ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "boring"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'hats' LIMIT 1
SQL (0.0ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "hats"]]
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Post' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
ActsAsTaggableOn::Tagging Exists (0.2ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 1 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.4ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 1], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
ActsAsTaggableOn::Tagging Exists (0.1ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 2 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.1ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 2], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
ActsAsTaggableOn::Tagging Exists (0.1ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 3 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.1ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 3], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
(1.9ms) commit transaction
=> true
1.9.3-p194 :004 > p.tag_list
=> ["news", "boring", "hats"]
1.9.3-p194 :005 > Post.tag_counts
ActsAsTaggableOn::Tag Load (0.4ms) SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: taggings.tag_id: SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `initialize'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `new'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `prepare'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:246:in `block in exec_query'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activesupport-3.2.8/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:242:in `exec_query'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:467:in `select'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/querying.rb:38:in `block in find_by_sql'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/explain.rb:40:in `logging_query_plan'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/querying.rb:37:in `find_by_sql'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:171:in `exec_queries'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:160:in `block in to_a'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/explain.rb:33:in `logging_query_plan'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:159:in `to_a'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:498:in `inspect'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands/console.rb:47:in `start'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands/console.rb:8:in `start'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'1.9.3-p194 :006 >
1.9.3-p194 :007 > exit
因此,我们似乎缺少 *taggings.tag_id* 列。因此,让我们进入 sqlite3 进行调查:
~/Development/foo [14:14:39]: sqlite3 db/development.sqlite3
SQLite version 3.7.14 2012-09-03 15:42:36
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .headers on
sqlite> select * from taggings;
id|tag_id|taggable_id|taggable_type|tagger_id|tagger_type|context|created_at
1|1|1|Post|||tags|2012-09-30 13:13:28.357091
2|2|1|Post|||tags|2012-09-30 13:13:28.360285
3|3|1|Post|||tags|2012-09-30 13:13:28.362351
sqlite> .schema taggings
CREATE TABLE "taggings" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "tag_id" integer, "taggable_id" integer, "taggable_type" varchar(255), "tagger_id" integer, "tagger_type" varchar(255), "context" varchar(128), "created_at" datetime);
CREATE INDEX "index_taggings_on_tag_id" ON "taggings" ("tag_id");
CREATE INDEX "index_taggings_on_taggable_id_and_taggable_type_and_context" ON "taggings" ("taggable_id", "taggable_type", "context");
sqlite>
嗯。所以专栏在那里!?让我们剪切并粘贴有问题的 SQL,看看我们得到相同的缺失列错误:
sqlite> SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id;
id|name|count
1|news|1
2|boring|1
3|hats|1
sqlite> .exit
~/Development/foo [14:17:25]:
什么……?有谁知道这里发生了什么。我错过了一些非常明显的事情吗?
更新(当天晚些时候):我找到了一个解决方法。覆盖 Posts 中的 *tag_counts* 类方法:
def self.tag_counts
ActsAsTaggableOn::Tag.select("tags.*, count(taggings.tag_id) as count").
joins(:taggings).group("taggings.tag_id")
end
感谢 Ryan Bates 和他出色的Railscasts 插曲作为此方法的起点。仍然有兴趣看看其他人是否可以重现该错误。