1

我是 Rails 的初学者(刚刚完成 Hartl 的教程),我在处理某些类型的关联时遇到了麻烦,这些关联似乎比我迄今为止接触过的关联更复杂。我一直在 google 和 stackoverflow 上浏览我的问题的解决方案,但是虽然我找到了许多相关的答案,但我仍然不知道如何解决我的问题。

基本上我要做的是实现以下关系:

ER图

在研究了它之后,我想出的是:

楷模

# models/user.rb
class User < ActiveRecord::Base
  attr_accessible :first_name, :last_name, :email …

  has_many :speak, :class_name => "Speaks"
  has_many :speaks, :through => :speak, :source => :language

  has_many :want_to_learn, :class_name => "WantsToLearn"
  has_many :wants_to_learn, :through => :want_to_learn, :source => :language
  …

end

# models/language.rb
class Language < ActiveRecord::Base
  attr_accessible :iso_639_1_code, :name_en, :name_fr, :name_pt

  has_many :speak, :class_name => "Speaks"
  has_many :users_who_speak, :through => :speak, :source => :user

  has_many :want_to_learn, :class_name => "WantsToLearn"
  has_many :users_who_want_to_learn, :through => :wants_to_learn, :source => :user
end

# models/speaks.rb
class Speaks < ActiveRecord::Base
  set_table_name 'speak'
  attr_accessible :level, :language
  references :user
  references :language
end

# models/wants_to_learn.rb
class WantsToLearn < ActiveRecord::Base
  set_table_name 'want_to_learn'
  attr_accessible :language
  references :user
  references :language
end

迁移

# db/migrate/20130210153328_create_speak.rb
class CreateSpeak < ActiveRecord::Migration
  def change
    create_table :speak do |t|
      t.references :user
      t.references :language
      t.integer :level

      t.timestamps
    end
  end
  add_index :speak, :user
  add_index :speak, :language
end

# db/migrate/20130210153342_create_want_to_learn.rb
class CreateWantToLearn < ActiveRecord::Migration
  def change
    create_table :want_to_learn do |t|
      t.references :user
      t.references :language

      t.timestamps
    end
  end
  add_index :want_to_learn, :user
  add_index :want_to_learn, :language
end

当我这样做并尝试运行rake:db migrate时,我收到以下错误:

-- add_index(:speak, :user)
rake aborted!
An error has occurred, this and all later migrations canceled:

PG::Error: ERROR:  relation "speak" does not exist
: CREATE  INDEX "index_speak_on_user" ON "speak" ("user")
...

如果我注释掉add_index迁移成功运行的行,但我无法从用户创建“说话”关系。例如,如果我运行:

> john = User.new(first_name: "John", last_name: "Doe", email: "john@doe.com"...)

> john.speaks.create!(level: '6', language: '139')

我得到:

NoMethodError: undefined method `references' for #<Class:0x007ff14c1771a0>

我试图用“belongs_to”和其他一些东西替换所有“引用”,其中许多我什至不记得了,但无济于事。这在我的脑海中提出了几个疑问,例如:

• “references”真的和“belongs_to”一样吗?即使在模型的背景下?我已经看到迁移中使用了“引用”,但从未见过模型。

• 我是否正确使用了“class_name”?我真的对此没有信心。这正是我阅读完所有内容后最有意义的,但我还没有在这样的关联中看到任何自定义示例。

• 添加这些索引有什么问题?

好吧,我不知道这里的礼仪是什么,但我的最后一个问题是:

我该如何正确地做这一切?

先感谢您!

------编辑------

我到处都用“belongs_to”替换了关键字“references”。索引的迁移错误完全相同。当我评论索引创建行时,运行迁移,创建用户,然后尝试命令

> john.speaks.create!(level: 6, language: 139)

错误是:

ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: level, language

尽管行attr_accessible :level, :language

4

2 回答 2

1

我是 Rails 中的 references 关键字的新手,但我在 Rails 指南的迁移中看到它,但在类中没有,所以不要在类中使用它。

因此,它与belongs_to 不同,因为belongs_to 用于类中,而引用用于迁移。

在创建索引时迁移会关心类中的关联对我来说也是新的。

于 2013-02-23T21:35:23.137 回答
1

好的。我终于设法让一切正常工作,我认为社区放弃未回答的问题不会很有建设性。我的代码现在可以完美运行,它包括我为了允许嵌套表单而必须进行的添加。这是它的外观:

楷模

# models/user.rb
class User < ActiveRecord::Base
  attr_accessible :first_name, :last_name, :email, ... ,
  :speaks_attributes, :wants_to_learn_attributes
  # These last two attributes are necessary for nested forms

  has_many :speaks, :class_name => "Speaks", :dependent => :destroy
  accepts_nested_attributes_for :speaks, :allow_destroy => true # this one too
  has_many :speaks_languages, :through => :speaks, :source => :language

  has_many :wants_to_learn, :class_name => "WantsToLearn", :dependent => :destroy
  accepts_nested_attributes_for :wants_to_learn, :allow_destroy => true # and this one too
  has_many :wants_to_learn_languages, :through => :wants_to_learn, :source => :language
  .
  .
  .

end

# models/language.rb
class Language < ActiveRecord::Base
  attr_accessible :iso_639_1_code, :name_en, :name_fr, :name_pt

  has_many :speak, :class_name => "Speaks"
  has_many :users_who_speak, :through => :speak, :source => :user

  has_many :want_to_learn, :class_name => "WantsToLearn"
  has_many :users_who_want_to_learn, :through => :want_to_learn, :source => :user
end

# models/speaks.rb
class Speaks < ActiveRecord::Base
  set_table_name 'speak'
  attr_accessible :language, :language_id, :level
  belongs_to :user
  belongs_to :language
end

# models/wants_to_learn.rb
class WantsToLearn < ActiveRecord::Base
  set_table_name 'want_to_learn'
  attr_accessible :language, :language_id
  belongs_to :user
  belongs_to :language
end

迁移

# db/migrate/20130210153328_create_speak.rb
class CreateSpeak < ActiveRecord::Migration
  def change
    create_table :speak, :id => false do |t|
      t.belongs_to :user
      t.belongs_to :language
      t.integer :level

      t.timestamps
    end
    add_index :speak, :user_id
    add_index :speak, :language_id
  end
end


# db/migrate/20130210153342_create_want_to_learn.rb
class CreateWantToLearn < ActiveRecord::Migration
  def change
    create_table :want_to_learn, :id => false do |t|
      t.belongs_to :user
      t.belongs_to :language

      t.timestamps
    end
    add_index :want_to_learn, :user_id
    add_index :want_to_learn, :language_id
  end
end

在迁移中,我将“add_index”行放在“def change”块内,因为上次我很愚蠢,心烦意乱地把它放在外面,并将“user”和“language”分别更改为“user_id”和“language_id”。那解决了它。

现在我可以创建一个用户ariel,并通过运行让他说葡萄牙语和法语:

> ariel.speaks.create!(language_id: 129, level: 6)
> ariel.speaks.create!(language_id: 50, level: 4)

ariel然后我可以通过运行获取与“说话”表中相关的条目:

> ariel.speaks
=> [#<Speaks id: 1, user_id: 1, language_id: 129, level: 6, created_at: "2013-02-25 19:30:01", updated_at: "2013-02-25 19:30:01">, #<Speaks id: 4, user_id: 1, la
nguage_id: 50, level: 4, created_at: "2013-02-26 12:37:34", updated_at: "2013-02-26 12:37:34">]

并通过运行“语言”表中的条目:

> ariel.speaks_languages
 => [#<Language id: 50, iso_639_1_code: "fr", name_en: "French", name_fr: "français\n", name_pt: nil>, #<Language id: 129, iso_639_1_code: "pt", name_en: "Portugu
ese", name_fr: "portugais\n", name_pt: nil>]

该过程类似于 Wants_to_learn 关联。

于 2013-02-26T13:10:05.187 回答