我可以在关注点中添加 has_many 和 belongs_to 关系的双方吗?

Can I add both sides of a has_many and belongs_to relationship in a concern?

编辑:回想起来,这并不是一个好主意。您正在将属于 ZipWithCBSA 的功能放入其他人的模型中。收到关注的模型按预期行事,ZipWithCBSA 响应 :store_locations 的事实在某种程度上应该是显而易见的 ZipWithCBSA。与其他models/concern无关。支持 Robert Nubel 用他的潜在解决方案使这一点显而易见。

是否可以在一个问题中同时处理 has_many 和 belongs_to 关系?

概述

我有一个 table ZipWithCBSA,它主要包括一堆邮政编码元信息。

我有两个型号有邮政编码:StoreLocationPriceSheetLocation。本质上:

class ZipWithCBSA < ActiveRecord::Base
  self.primary_key = :zip
  has_many :store_locations, foreign_key: :zip
  has_many :price_sheet_locations, foreign_key: :zip
end

class StoreLocation< ActiveRecord::Base
  belongs_to :zip_with_CBSA, foreign_key: :zip
  ...
end

class PriceSheetLocation < ActiveRecord::Base
  belongs_to :zip_with_CBSA, foreign_key: :zip
  ...
end

ZipWithCBSA 中有两个属性我一直希望与我的其他模型一起返回,包括在 #index 页面上。为了防止每次查询时都为每个项目加入这个 table,我想将其中两个字段缓存到模型本身中——即

# quick schema overview~~~
ZipWithCBSA
  - zip
  - cbsa_name
  - cbsa_state
  - some_other_stuff
  - that_I_usually_dont_want
  - but_still_need_access_to_occasionally

PriceSheetLocation
  - store
  - zip
  - cbsa_name
  - cbsa_state

StoreLocation
  - zip
  - generic_typical
  - location_address_stuff
  - cbsa_name
  - cbsa_state

所以,我添加了

after_save :store_cbsa_data_locally, if: ->(obj){ obj.zip_changed? }
private
def store_cbsa_data_locally
  if zip_with_cbsa.present?
    update_column(:cbsa_name, zip_with_cbsa.cbsa_name)
    update_column(:cbsa_state, zip_with_cbsa.cbsa_state)
  else
    update_column(:cbsa_name, nil)
    update_column(:cbsa_state, nil)
  end
end

我希望将这些问题转化为问题,所以我做到了:

# models/concerns/UsesCBSA.rb

module UsesCBSA
  extend ActiveSupport::Concern

  included do
    belongs_to :zip_with_cbsa, foreign_key: 'zip'

    after_save :store_cbsa_data_locally, if: ->(obj){ obj.zip_changed? }

    def store_cbsa_data_locally
      if zip_with_cbsa.present?
        update_column(:cbsa_name, zip_with_cbsa.cbsa_name)
        update_column(:cbsa_state, zip_with_cbsa.cbsa_state)
      else
        update_column(:cbsa_name, nil)
        update_column(:cbsa_state, nil)
      end
    end

    private :store_cbsa_data_locally

  end
end


# ~~models~~
class StoreLocation < ActiveRecord::Base
  include UsesCBSA
end

class PriceSheetLocation < ActiveRecord::Base
  include UsesCBSA
end

这一切都很好——但我仍然需要手动将 has_many 关系添加到 ZipWithCBSA 模型:

class ZipWithCBSA < ActiveRecord::Base
  has_many :store_locations, foreign_key: zip
  has_many :price_sheet_locations, foreign_key: zip
end

终于!问题!

是否可以重新打开 ZipWithCBSA 并添加来自关注的 has_many 关系?或者以任何其他更自动的方式允许我一次指定这些特定系列的模型是 bffs?

我试过了

# models/concerns/uses_cbsa.rb
...
def ZipWithCBSA
  has_many self.name.underscore.to_sym, foregin_key: :zip
end
...

还有

# models/concerns/uses_cbsa.rb
...
ZipWithCBSA.has_many self.name.underscore.to_sym, foregin_key: :zip
...

但都没有用。我猜这与模型自身初始化期间未添加这些关系有关...但是 是否可以在单独的文件中定义模型的关系?

我还不是很擅长 Ruby 的元编程。

您最好的选择可能是在您的关注点包含在相关模型中时将关系添加到您的 ZipWithCBSA 模型中,在模型 class 本身上使用 class_exec。例如,在您关注的 included 块内,添加:

relation_name = self.table_name
ZipWithCBSA.class_exec do
  has_many relation_name, foreign_key: :zip
end