Rails 5:STI 具有许多关联性

问题描述 投票:0回答:1

我已经广泛寻找适合我的情况的解决方案,但我找不到任何东西。

在我的应用程序中,我有一个

Person
模型,它存储有关人员的数据:

class Person < ApplicationRecord
end

然后我就有了一个

Trial
模型。试验可以有很多人使用 has-many-through 关联。此外,在审判中,一个人可以是被告原告。为了实现这一目标,我这样设置模型:

class Trial < ApplicationRecord
  has_many :trial_people
  has_many :plaintiffs, class_name: 'Plaintiff', through: :trial_people, source: :person
  has_many :defendants, class_name: 'Defendant', through: :trial_people, source: :person
end

class TrialPerson < ApplicationRecord
  belongs_to :trial
  belongs_to :person
end

class Plaintiff < Person
end

class Defendant < Person
end

然后我使用 Select2 JQuery 插件在视图中添加每次审判的被告和原告。获取强参数中的ID:

params.require(:trial).permit(:title, :description, :start_date, :plaintiff_ids => [], :defendant_ids => [])

这样我就可以实现如下目标:

trial.defendants
trial.plaintiffs

问题是我没有任何方法来区分

trial_people
表中的这些类。我正在考虑向该表(STI)添加一个
type
列,但我不知道在保存 Trial 对象时如何自动将该类型添加到每个被告或原告。

希望了解如何实现这一目标(无论是否使用 STI)的一些见解。

ruby-on-rails activerecord associations has-many-through sti
1个回答
3
投票

无需更改关联或模式即可实现此目的的一种方法是使用

before_create
回调。

假设您已将

person_type
字符串列添加到
trial_people

class TrialPerson < ApplicationRecord
  belongs_to :trial
  belongs_to :person
  before_create :set_person_type

  private

  def set_person_type
    self.person_type = person.type
  end
end

解决该问题的另一种方法是删除

person
关联并用多态
triable
关联替换。这实现了相同的最终结果,但它内置于 ActiveRecord API 中,因此不需要任何回调或额外的自定义逻辑。

# migration

class AddTriableReferenceToTrialPeople < ActiveRecord::Migration
  def up
    remove_reference :trial_people, :person, index: true
    add_reference :trial_people, :triable, polymorphic: true
  end
  
  def down
    add_reference :trial_people, :person, index: true
    remove_reference :trial_people, :triable, polymorphic: true
  end
end

# models

class TrialPerson < ApplicationRecord
  belongs_to :trial
  belongs_to :triable, polymorphic: true
end

class Person < ApplicationRecord
  abstract_class = true
  has_many :trial_people, as: :triable
end

class Trial < ApplicationRecord
  has_many :trial_people
  has_many :defendants, source: :triable, source_type: 'Defendant', through: :trial_people
  has_many :plaintiffs, source: :triable, source_type: 'Plaintiff', through: :trial_people
end

class Plaintiff < Person
end

class Defendant < Person
end

这会在

triable_type
表上为您提供
triable_id
trial_people
列,当您添加到集合时会自动设置这些列

trial = Trial.create
trial.defendants << Defendant.first
trial.trial_people.first # => #<TrialPerson id: 1, trial_id: 1, triable_type: "Defendant", triable_id: 1, ... >

请注意,Person 必须是一个抽象类,否则

triable_type
将为包括原告和被告在内的所有 Person 对象设置为“Person”,除非手动覆盖。

© www.soinside.com 2019 - 2024. All rights reserved.