关于 rails 方式关系模型

About the rails way relation models

上下文:

我有两个 table,challengeschallenge_steps。两个 table 之间都需要有关系,我需要能够引用 StepChallenge 以及相反的关系。

一个 challenge 可以有多个 steps 但只有一个 current_step.

架构:

Challenge:

t.string   "name"
t.string   "subtitle"
t.text     "brief",                     null: false
t.integer  "min_team_size", default: 2, null: false
t.integer  "max_team_size", default: 5, null: false
t.datetime "created_at",                null: false
t.datetime "updated_at",                null: false

Challenge::Step:

t.integer  "challenge_id"
t.string   "name"
t.text     "description"
t.datetime "start_at"
t.datetime "end_at"
t.datetime "created_at",   null: false
t.datetime "updated_at",   null: false

要做到这一点,我可以想到三个解决方案,但其中 none 个令人满意:

解决方案一:

Challenge 型号:

  has_many :steps, inverse_of: :challenge, dependent: :destroy
  belongs_to :current_step, class_name: Challenge::Step

Challenge::Step:

  belongs_to :challenge
  has_one :challenge_relation, class_name: Challenge,
                               foreign_key: :current_step_id, dependent: :restrict_with_error

正如您在我的 Challenge::Step 模型中看到的那样,我有一个 belongs_to(:challenge) 并且 Rails 文档显示:

For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier.

所以行为没问题,但代码看起来很奇怪。

解决方案二:

创建一个包含 challenge_idstep_id 的 table。这将引用每个 challenge 及其 current_step

这个很好,但这意味着我们需要阅读另一个 table 以获得所需的信息。

方案三:

Challenge型号中加入:

has_many :steps, inverse_of: :challenge, dependent: :destroy do
    def current_step
      proxy_association.owner.steps.where(current_step: true).first
    end
end

它 returns 集合和架构不尊重挑战与其步骤之间的真实关系。

什么最高效优雅?您能想出一个解决方案来解决 none 这些缺点吗?

我认为第一个解决方案是'The Rails Way'做你需要的。

也许唯一的缺点是代码可读性,从字面意义上讲,挑战不属于当前步骤,但我认为在线上的注释应该足够了,因为那时,界面访问它真的很有意义:challenge.current_step

首先,为什么Challenge::StepChallenge的子类?

您肯定希望它单独 Step 吗?为了清楚起见,我将其称为 Step.

--

我会这样做:

#app/models/challenge.rb
class Challenge < ActiveRecord::Base
    has_many :steps
    def current
        steps.where(current: true).order(current: :desc).first 
    end
end

#app/models/step.rb
class Step < ActiveRecord::Base
   # columns id | challenge_id | current (datetime) | etc...
   belongs_to :challenge
end

这将使您能够调用:

@challenge = Challenge.find params[:id]
# @challenge.steps = collection of steps
# @challenge.current_step = latest current step

想法是您可以将 current_step 属性保存为 Step 模型中的日期。这将有额外的好处,让您能够查看每一步的历史记录 "current".

--

另一种方法是在 Challenge 模型中创建一个 current 列:

#app/models/challenge.rb
class Challenge < ActiveRecord::Base
   # columns id | name | current | etc
   has_many :steps
   def current_step
      steps.find current
   end
end

#app/models/step.rb
class Step < ActiveRecord::Base
   #columns id | challenge_id | name | etc
   belongs_to :challenge
end

这将允许您调用以下内容:

@challenge = Challenge.find params[:id]
# @challenge.steps = collection of steps
# @challenge.current_step = single instance of step

--

你的第三个解决方案是迄今为止最优雅的,但它假定你实现的结构是正确的。

我认为您没有正确的设置来处理 current_step 属性;您需要一种方法在 Step 模型或 Challenge 模型中区分它。