如何创建自动设置连接表属性的关联?

时间:2013-06-12 22:35:33

标签: ruby-on-rails ruby model associations ruby-on-rails-4

我对如何有效地使用我的关联的“轨道方式”感到困惑。

以下是Rails 4应用程序的示例模型配置:

class Film < ActiveRecord::Base
  # A movie, documentary, animated short, etc
  has_many :roleships
  has_many :participants, :through => :roleships
  has_many :roles, :through => :roleships
  # has_many :writers........ ?
end

class Participant < ActiveRecord::Base
  # A human involved in making a movie
  has_many :roleships
end

class Role < ActiveRecord::Base
  # A person's role in a film. i.e. "Writer", "Actor", "Extra" etc
  has_many :roleships
end

class Roleship < ActiveRecord::Base
  # The join for connecting different people
  # to the different roles they have had in
  # different films
  belongs_to :participant
  belongs_to :film
  belongs_to :role
end

鉴于上面的模型配置,我希望我拥有的代码可以让我直接将作者添加到电影中,并最终正确地进行连接设置。

例如,我希望能够做到这样的事情:

## The Code I WISH I Had
Film.create!(name: "Some film", writers: [Participant.first])

我不确定我是否会考虑这完全错误,但似乎不可能。完成此任务的正确方法是什么?嵌套资源?自定义setter +范围?别的什么?虚拟属性?谢谢!

2 个答案:

答案 0 :(得分:3)

我根据您的问题创建了一个示例应用。 https://github.com/szines/hodor_filmdb

我认为通过关联在参与者和角色模型中设置是有用的,但如果没有这个将起作用。这取决于您希望以后如何使用此数据库。如果没有通过此查询将不起作用:Participant.find(1).films

class Participant < ActiveRecord::Base
  has_many :roleships
  has_many :films, through: :roleships
end

class Role < ActiveRecord::Base
  has_many :roleships
  has_many :films, through: :roleships
end

不要忘记在films_controller.rb中为额外字段(strong_parameters)提供许可

def film_params
  params.require(:film).permit(:title, :participant_ids, :role_ids)
end

奇怪的是,如果您创建一个包含参与者和角色的新电影,则会在连接表中创建两条记录。

希望这有帮助,有人可以帮助我们进一步改进......如果您有任何疑问,请告诉我。

<强>更新

您可以在模型中创建一种虚拟属性。例如:

def writers=(participant)
  @writer_role = Role.find(1)
  self.roles << @writer_role
  self.participants << participant
end

您可以使用:Film.create(title: 'The Movie', writers: [Participant.first])

答案 1 :(得分:0)

如果你有一个正常的has_and_belongs_to_many关系,即电影和参与者之间的关系,那么你可以和你的例子一起创作一部电影。

由于您的加入模式更复杂,您必须单独构建角色:

writer= Roleship.create(
          participant: Participant.find_by_name('Spielberg'),
          role: Role.find_by_name('Director')
        )
main_actor= Roleship.create(
          participant: Participant.find_by_name('Willis'),
          role: Role.find_by_name('Actor')
        )

Film.create!(name: "Some film", roleships: [writer, main_actor])

为此,您用于构建角色和影片的所有属性必须是可批量分配的,因此在Rails 3.2中您必须编写:

class Roleship < ActiveRecord::Base
   attr_accessible :participant, :role
   ...
end

class Film < ActiveRecord::Base
   attr_accessible :name, :roleships
   ...
end

如果您想使用roleship_ids,则必须编写

class Film < ActiveRecord::Base
   attr_accessible :name, :roleship_ids
   ...
end

附录:

原因你可以写一个setter方法

class Film ...

  def writers=(part_ids)
     writer_role=Role.find_by_name('Writer')
     # skiped code to delete existing writers
     part_ids.each do |part_id|
        self.roleships << Roleship.new(role: writer_role, participant_id: part_id)
     end
  end
end

但这会使您的代码依赖于数据库中的数据(表roles的内容),这是一个坏主意。