我是Rails的初学者(刚刚完成了Hartl的教程),我遇到了一些类型的关联,这些关联看起来比我到目前为止所遇到的要复杂一些。我一直在谷歌和stackoverflow浏览我的问题的解决方案,但虽然我发现了许多相关的答案,我仍然无法弄清楚如何解决我的问题。
基本上我要做的是实现以下关系:
在研究之后,我想出的是:
模型
# 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”和其他一些东西替换所有“引用”,其中许多我甚至都不记得,但无济于事。这引起了我的一些疑虑,例如:
•“引用”是否与“belongs_to”完全相同?即使在模特的背景下?我已经看到“引用”已用于迁移但从未使用模型。
•我正确使用“class_name”吗?我对它真的没有信心。这是我阅读的所有内容之后最有意义的内容,但我没有在这样的协会中看到任何自定义示例。
•添加这些索引有什么问题?
而且,我不知道这里的礼节是什么,但我的最后一个问题是:
我如何正确地做到这一切?
提前谢谢!
------ 编辑 ------
我将关键字“references”替换为“belongs_to”。有关索引的迁移错误完全相同。当我评论索引创建行时,运行迁移,创建用户,然后尝试命令
> john.speaks.create!(level: 6, language: 139)
错误是:
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: level, language
尽管有attr_accessible :level, :language
行。
答案 0 :(得分:1)
我是Rails中的references关键字的新手,但我在Rails指南中的迁移中看到它,但在类中没有,所以不要在类中使用它。
因此,它与belongs_to不同,因为belongs_to在类中使用,并且引用用于迁移。
对我来说,迁移会在创建索引时关心类中的关联。
答案 1 :(得分: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)
然后我可以通过运行:
获取“speak”表中与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关联。