【问题标题】:Rails 4 - Has_many through - StatementInvalid - SQLite3::SQLException: no such column:Rails 4 - Has_many 通过 - StatementInvalid - SQLite3::SQLException:没有这样的列:
【发布时间】:2015-10-08 22:08:21
【问题描述】:

我已经在这个问题上苦苦挣扎了 4 天,我想知道我是否没有遇到 ActiveRecord 错误?我正在尝试将用户模型链接到标注模型。

user.rb

class User < ActiveRecord::Base
    has_many :callouts_users
    has_many :callouts, through: :callouts_users    
    devise  :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable     
    has_many :posts, inverse_of: :creator
    has_many :callouts, as: :calloutable    
    has_many :profiles, as: :profileable, :validate => true     
    validates :name, presence: true
    validates_uniqueness_of :name, :case_sensitive => false, :message => "This name has already been taken"
end

callout.rb

class Callout < ActiveRecord::Base
    has_many :callouts_users
    has_many :users, through: :callouts_users
    belongs_to :conversation
    belongs_to :calloutable, polymorphic: true, class_name: "::Callout", :validate => true  
    validates :conversation, presence: true
    validates :calloutable, presence: true
    validates_uniqueness_of :calloutable_id, :scope => [:user_id, :conversation_id, :calloutable_type]
end

user_callout.rb

class UserCallout < ActiveRecord::Base
    belongs_to :user
    belongs_to :callout
    validates :type, presence: true, 
    validates :type, :inclusion=> { :in => ["up", "down"] }
end

我的迁移如下:

..._create_callouts_users.rb

class CreateCalloutsUsers < ActiveRecord::Migration
  def change
    create_table :callouts_users do |t|
      t.timestamps null: false
    end
  end
end

..._add_callout_to_callouts_users.rb

class AddCalloutToCalloutsUsers < ActiveRecord::Migration
  def change
    add_reference :callouts_users, :callout, index: true
    add_foreign_key :callouts_users, :callouts
  end
end

..._add_user_to_callouts_users.rb

class AddUserToCalloutsUsers < ActiveRecord::Migration
  def change
    add_reference :callouts_users, :user, index: true
    add_foreign_key :callouts_users, :users
  end
end

当我尝试做类似的事情时

@callout = @conversation.callouts.find_by(calloutable: @user) 
if(@callout.nil?) @callout = Callout.new(conversation: @conversation, calloutable: @user)
@callout.users << current_user
@callout.save

我马上就有了: CalloutsController#create 中的 ActiveRecord::StatementInvalid SQLite3::SQLException: 没有这样的列: callouts.user_id: SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"."conversation_id" IS NULL AND "callouts"."calloutable_type" IS NULL) LIMIT 1

好像 ActiverRecords 在我的标注表上寻找“user_id”列,而 user_id 仅在连接表一侧...... 我在我的模型上做错了什么?为什么我的 has_many - 槽关联不被识别?

这里是生成的 SQL 代码:

用户存在 (0.2ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."name") = LOWER('name10') AND "users"."id" != 10) LIMIT 1

Callout Exists (0.6ms) SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"."conversation_id" = 1 AND " callouts"."calloutable_type" IS NULL) LIMIT 1

SQLite3::SQLException: 没有这样的列:callouts.user_id: SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"。 "conversation_id" = 1 AND "callouts"."calloutable_type" IS NULL) LIMIT 1

(0.0ms) 回滚事务

在 50 毫秒内完成 500 个内部服务器错误

ActiveRecord::Schema.define(version: 20150720002524) do

  create_table "callouts", force: :cascade do |t|
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.integer  "conversation_id"
    t.integer  "calloutable_id"
    t.string   "calloutable_type"
  end

  add_index "callouts", ["calloutable_type", "calloutable_id"], name: "index_callouts_on_calloutable_type_and_calloutable_id"
  add_index "callouts", ["conversation_id"], name: "index_callouts_on_conversation_id"

  create_table "callouts_users", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "user_id"
    t.integer  "callout_id"
  end

  add_index "callouts_users", ["callout_id"], name: "index_callouts_users_on_callout_id"
  add_index "callouts_users", ["user_id"], name: "index_callouts_users_on_user_id"

  create_table "conversations", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "posts", force: :cascade do |t|
    t.datetime "created_at",      null: false
   t.datetime "updated_at",      null: false
    t.integer  "conversation_id"
    t.integer  "creator_id"
    t.text     "title"
    t.text     "content"
 end

  add_index "posts", ["conversation_id"], name: "index_posts_on_conversation_id"
  add_index "posts", ["creator_id"], name: "index_posts_on_creator_id"

  create_table "potential_users", force: :cascade do |t|
    t.datetime "created_at", null: false
   t.datetime "updated_at", null: false
  end

  create_table "profiles", force: :cascade do |t|
   t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.integer  "profileable_id"
    t.string   "profileable_type"
    t.string   "description"
 end

  add_index "profiles", ["description"], name: "index_profiles_on_description", unique: true
  add_index "profiles", ["profileable_type", "profileable_id"], name: "index_profiles_on_profileable_type_and_profileable_id"

  create_table "users", force: :cascade do |t|
    t.datetime "created_at",                          null: false
    t.datetime "updated_at",                          null: false
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.string   "name"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

end

================================================ ====> 我已经在这个问题上苦苦挣扎了 4 天,我想知道我是否没有遇到 ActiveRecord 错误?我正在尝试将用户模型链接到标注模型。

user.rb

class User < ActiveRecord::Base
    has_many :callouts_users
    has_many :callouts, through: :callouts_users    
    devise  :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable     
    has_many :posts, inverse_of: :creator
    has_many :callouts, as: :calloutable    
    has_many :profiles, as: :profileable, :validate => true     
    validates :name, presence: true
    validates_uniqueness_of :name, :case_sensitive => false, :message => "This name has already been taken"
end

callout.rb

class Callout < ActiveRecord::Base
    has_many :callouts_users
    has_many :users, through: :callouts_users
    belongs_to :conversation
    belongs_to :calloutable, polymorphic: true, class_name: "::Callout", :validate => true  
    validates :conversation, presence: true
    validates :calloutable, presence: true
    validates_uniqueness_of :calloutable_id, :scope => [:user_id, :conversation_id, :calloutable_type]
end

user_callout.rb

class UserCallout < ActiveRecord::Base
    belongs_to :user
    belongs_to :callout
    validates :type, presence: true, 
    validates :type, :inclusion=> { :in => ["up", "down"] }
end

我的迁移如下:

..._create_callouts_users.rb

class CreateCalloutsUsers < ActiveRecord::Migration
  def change
    create_table :callouts_users do |t|
      t.timestamps null: false
    end
  end
end

..._add_callout_to_callouts_users.rb

class AddCalloutToCalloutsUsers < ActiveRecord::Migration
  def change
    add_reference :callouts_users, :callout, index: true
    add_foreign_key :callouts_users, :callouts
  end
end

..._add_user_to_callouts_users.rb

class AddUserToCalloutsUsers < ActiveRecord::Migration
  def change
    add_reference :callouts_users, :user, index: true
    add_foreign_key :callouts_users, :users
  end
end

当我尝试做类似的事情时

@callout = @conversation.callouts.find_by(calloutable: @user) 
if(@callout.nil?) @callout = Callout.new(conversation: @conversation, calloutable: @user)
@callout.users << current_user
@callout.save

我马上就有了: CalloutsController#create 中的 ActiveRecord::StatementInvalid SQLite3::SQLException: 没有这样的列: callouts.user_id: SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"."conversation_id" IS NULL AND "callouts"."calloutable_type" IS NULL) LIMIT 1

好像 ActiverRecords 在我的标注表上寻找“user_id”列,而 user_id 仅在连接表一侧...... 我在我的模型上做错了什么?为什么我的 has_many - 槽关联不被识别?

这里是生成的 SQL 代码:

用户存在 (0.2ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."name") = LOWER('name10') AND "users"."id" != 10) LIMIT 1

Callout Exists (0.6ms) SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"."conversation_id" = 1 AND " callouts"."calloutable_type" IS NULL) LIMIT 1

SQLite3::SQLException: 没有这样的列:callouts.user_id: SELECT 1 AS one FROM "callouts" WHERE ("callouts"."calloutable_id" IS NULL AND "callouts"."user_id" IS NULL AND "callouts"。 "conversation_id" = 1 AND "callouts"."calloutable_type" IS NULL) LIMIT 1

(0.0ms) 回滚事务

在 50 毫秒内完成 500 个内部服务器错误

ActiveRecord::Schema.define(version: 20150720002524) do

  create_table "callouts", force: :cascade do |t|
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.integer  "conversation_id"
    t.integer  "calloutable_id"
    t.string   "calloutable_type"
  end

  add_index "callouts", ["calloutable_type", "calloutable_id"], name: "index_callouts_on_calloutable_type_and_calloutable_id"
  add_index "callouts", ["conversation_id"], name: "index_callouts_on_conversation_id"

  create_table "callouts_users", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "user_id"
    t.integer  "callout_id"
  end

  add_index "callouts_users", ["callout_id"], name: "index_callouts_users_on_callout_id"
  add_index "callouts_users", ["user_id"], name: "index_callouts_users_on_user_id"

  create_table "conversations", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "posts", force: :cascade do |t|
    t.datetime "created_at",      null: false
   t.datetime "updated_at",      null: false
    t.integer  "conversation_id"
    t.integer  "creator_id"
    t.text     "title"
    t.text     "content"
 end

  add_index "posts", ["conversation_id"], name: "index_posts_on_conversation_id"
  add_index "posts", ["creator_id"], name: "index_posts_on_creator_id"

  create_table "potential_users", force: :cascade do |t|
    t.datetime "created_at", null: false
   t.datetime "updated_at", null: false
  end

  create_table "profiles", force: :cascade do |t|
   t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.integer  "profileable_id"
    t.string   "profileable_type"
    t.string   "description"
 end

  add_index "profiles", ["description"], name: "index_profiles_on_description", unique: true
  add_index "profiles", ["profileable_type", "profileable_id"], name: "index_profiles_on_profileable_type_and_profileable_id"

  create_table "users", force: :cascade do |t|
    t.datetime "created_at",                          null: false
    t.datetime "updated_at",                          null: false
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.string   "name"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

end

==============================> 编辑

好的,

我还有一个线索。我认为错误在于用户模型,但我不知道如何编写。

简而言之:

1.) 不同的用户可以参与不同的标注。所以 User Callout 是一个“has_many / through”关系。 (不是HABTM,因为我需要自定义连接模型)。所以我可以写@callout.users

2.) 一个 Callout 以一个 Calloutable 为目标(Calloutable 是 User 或 PotentialUser)。但是一个 Calloutable 可能会被不同的 Callouts 定位。所以它是一个belong_to / has_many 多态关系。我可以写@user.callouts...也可以写@callout.users...

但是:@callout.users 在情况 1) 或 2) 中的含义不同。

以下是详细型号:

class Callout < ActiveRecord::Base
    has_many :callouts_users
    has_many :users, through: :callouts_users

    belongs_to :calloutable, polymorphic: true, class_name: "::Callout", :validate => true    
    validates :calloutable, presence: true
    validates_uniqueness_of :calloutable_id, :scope => [:user_id, :conversation_id, :calloutable_type]

    belongs_to :conversation
    validates :conversation, presence: true
end

class CalloutsUser < ActiveRecord::Base
    belongs_to :user
    belongs_to :callout
    validates_uniqueness_of :user_id, :scope => [:callout_id]
end

class User < ActiveRecord::Base
    has_many :callouts_users
    has_many :callouts, through: :callouts_users
    has_many :callouts, as: :calloutable

    devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
    has_many :posts, inverse_of: :creator    
    has_many :profiles, as: :profileable, :validate => true 

    validates :name, presence: true
    validates_uniqueness_of :name, :case_sensitive => false, :message => "This name has already been taken"
end

class PotentialUser < ActiveRecord::Base
    has_many :callouts, as: :calloutable      
    has_one :profile, as: :profileable, :validate => true
    validates :profile, presence: true
end

有重写用户模型部分的想法吗? (2 has_many :callouts)我想我只需要在用户收到的@user.callouts 和用户制作的@user.callouts 之间做出改变......

【问题讨论】:

  • 我还没有通读一遍,但看起来您使用user_callouts 作为连接表或合并表。不要与惯例作对。 Rails 希望您将其命名为 callouts_users。这将消除您对:through 的要求
  • 您可能需要添加什么来阻止您根据框架定义habtm?该表将只是以不同的方式命名。没有其他任何变化或变得不可用。
  • 如果我不清楚或您误解了,您可以删除 :through 的原因是,如果您只是正确命名表,它将变得不必要。跨度>
  • 不幸的是,我需要 has_many / through 关联,因为我必须在 callouts_users 表上添加一些东西。我知道 Rails 命名约定,但它只适用于 HBTM,不是吗? (对于HM through,应该可以选择型号名称?)为了确定,我已将user_callouts 重命名为callouts_users,但没有任何改变。 Rails 错过了连接表,并在标注时一直在寻找“user_id”......我需要访问连接的模型以将其添加到其他列。
  • 没有什么可以阻止您为合并表添加模型,其中包含您想要的任何条件。

标签: ruby-on-rails ruby-on-rails-4 activerecord sqlite has-many-through


【解决方案1】:

已经解决了!! 问题实际上是我写的:

has_many :callouts_users
has_many :callouts, through: :callouts_users
has_many :callouts, as: :calloutable

所以我定义了has_many :callouts, 两次。当然,Rails 不知道如何理解@callout.users

与:

has_many :callouts_users
has_many :callouts, through: :callouts_users, source: "call", class_name: "Call"
has_many :callins, as: :callable, class_name: "Call"`

我工作完美! 感谢您对我这个新手的耐心和理解...... :-)

【讨论】:

    【解决方案2】:

    我觉得你让这件事变得比需要的更难。不要与框架抗争,它可以提供帮助!

    首先,对于您的连接表,使用标准的 Rails 命名约定:按字母顺序排列模型。如果您回滚一点,而不是为一个操作创建三个迁移,为什么不:

    rails g migration callouts_users callout_id:integer user_id:integer

    再加上模型中的 has_and_belongs_to_many 关系,你应该可以开始了。

    【讨论】:

    • 事实上,Rails 中的命名约定仅适用于 HABTM 表。对于 has_many/through,可以按照您想要的方式命名模型...
    猜你喜欢
    • 2013-10-09
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 2016-01-19
    相关资源
    最近更新 更多