如何查询多态关系

时间:2014-01-31 17:48:47

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

我有一个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

给定作业的状态由与其相关的最后一个活动的状态决定,如下所示(本例中的最后一个是最后创建的,因此我们可以按ID安全地排序)。

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

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

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

after_create :update_state

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

这会以一种更简单的方式解决问题吗?

1 个答案:

答案 0 :(得分:0)

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


状态机

使用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

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

最重要的方面是使用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