【问题标题】:How to query a polymorphic relationship如何查询多态关系
【发布时间】:2014-01-31 17:48:47
【问题描述】:

我有一个jobs 表和一个activities 表。一份工作可以有许多活动。关系是多态的。

表格:

job         activities
----------- -------------
id          id
...         target_id
            target_type
            ...

模型伪代码:

class Job < ActiveRecord::Base
  # ...
  has_many :activities, :as => :target, :dependent => :delete_all
  # ...
end

class Activity < ActiveRecord::Base
  # ...
  belongs_to :target, :polymorphic => true
  # ...
end

给定 Job 的状态由它关联的最后一个 Activity 的状态决定,如下所示(在这种情况下,最后一个是最后一个创建的,因此我们可以安全地按 id 排序)。

Job +-> Activity 1 - state: new        |
    +-> Activity 2 - state: submitted  +--> Job state is approved
    `-> Activity 3 - state: approved   |

如何使用 ActiveRecord 和 Rails 3/4 仅查询处于给定状态的作业?

我还(只是)想到了一个想法,即使用“计数器缓存”之类的东西并非规范化关系,而不是完全执行此查询。我会将状态存储在作业表上并使用Activity 模型上的after_create 挂钩保持同步?类似这样的事情:

after_create :update_state

def update_state
  target.update_attribute(state: self.state) if target.respond_to?(:state)
end

这是否是一种以更简单的方式解决问题的可行替代方案?

【问题讨论】:

标签: ruby-on-rails ruby-on-rails-3 activerecord


【解决方案1】:

我不认为多态在这里会成为问题(这只是选择正确数据的一种方式)


状态机

如何使用state machine

从外观上看,您对模型进行了过度设计。你的Job 有状态,而不是activity,那么为什么要使用另一个模型来复杂化呢?

如果你使用状态机,对象的状态会被原生处理,像这样:

#app/models/job.rb
Class Job < ActiveRecord::Base

state_machine :initial => :new do

        #States
        state :new
        state :submitted
        state :approved

        ### Events ###

        #Submit
        event :submit do
        transition :new => :submitted
        end

        #Approve
        event :approve do
        transition :submitted => :approved
        end
    end

end

这将直接在您的作业模型中使用state 属性处理Job 状态

这其中最重要的一点是state-machine events的使用:

#State Machine Functions
@job = Job.find(id)

@job.state        #-> "new"
@job.new?         #-> false
@job.submitted?   #-> false
@job.approved?    #-> true

@job.submit!      #-> state: "new" -> "submitted"
@job.approve!     #-> state: "submitted" -> "approved"

范围

如果您不想删除 activity 模型,您可能希望改用 scopes

虽然我知道你可以使用它,但我不确定多态关联的语法是否正确:

#app/models/job.rb
Class Job < ActiveRecord::Base
    scope :submitted, -> { joins(:activity).where(state: "submitted") }
    scope :activity, ->(activity = new) { joins(:activity).where(state: activity) }
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    • 2013-08-28
    • 1970-01-01
    • 2013-10-11
    • 2016-12-15
    • 2017-02-02
    相关资源
    最近更新 更多