【问题标题】:How to create an association that sets join table attributes automatically?如何创建自动设置连接表属性的关联?
【发布时间】:2013-06-09 05:11:00
【问题描述】:

我完全不知道我应该如何去有效地使用我的联想的“轨道方式”。

这是来自 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])

我不确定我是否要考虑这个完全错误的想法,但这似乎是不可能的。实现这一目标的正确方法是什么?嵌套资源?自定义设置器+范围?还有什么?虚拟属性?谢谢!

【问题讨论】:

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


    【解决方案1】:

    我根据您的问题创建了一个示例应用。 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])

    【讨论】:

    • 非常感谢您将其放入真正的应用程序和存储库中。我实际上并没有在参与者和角色模型上投入精力,因为构建它们与我的问题无关。基本上我想知道我应该如何设置我的关联以支持编写类似于“我希望我拥有的代码”下的代码
    • 好的。我知道了。你可以在seed.rb 中看到一个例子。的确,在我的解决方案中,角色可能是动态的,而不仅仅是作家。
    【解决方案2】:

    如果您有正常的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 的内容),这是一个坏主意。

    【讨论】:

    • 这不会对未来产生不利影响吗?例如,如果有嵌套的表单(即从视图构建电影),这一切都必须被推入一个胖控制器吗?另外,正如我提到的,这是一个 rails4 应用程序(使用强参数)
    • 您可以将角色的创建推送到模型,然后您必须将所有必要的参数传递给该创建函数。这样您就可以使用某种“自定义嵌套属性”构建表单
    • 我想我在概念上理解了,但我无法准确地设想代码会是什么样子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多