【问题标题】:ActiveRecord has_many through two modelsActiveRecord has_many 通过两个模型
【发布时间】:2018-03-14 17:42:08
【问题描述】:

我有以下上下文:

4 种型号:

  • 项目
  • 投资者
  • 订阅
  • 外部订阅

一个project 应该有许多investorssubscriptionsexternal_subscriptions

我目前有一个方法可以做这样的事情:Investor.where(id: (subscription_ids + external_subscription_ids))

我的目标是建立has_many 关系(并精确地使用has_many activerecord 功能)以获得相同的结果。 我怎样才能做到这一点?有没有可能?

谢谢!

Project

[Associations]
  has_many :subscriptions
  has_many :external_subscriptions

[Table description]
  create_table "projects", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

Investor

[Associations]
  has_many :subscriptions
  has_many :external_subscriptions

[Table description]
  create_table "investors", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

Subscription

[Associations]
  belongs_to :project
  belongs_to :investor

[Table description]
  create_table "subscriptions", force: :cascade do |t|
    t.integer "project_id"
    t.integer "investor_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["investor_id"], name: "index_subscriptions_on_investor_id"
    t.index ["project_id"], name: "index_subscriptions_on_project_id"
  end

ExternalSubscription

[Associations]
  belongs_to :project
  belongs_to :investor

[Table description]
  create_table "external_subscriptions", force: :cascade do |t|
    t.integer "project_id"
    t.integer "investor_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["investor_id"], name: "index_external_subscriptions_on_investor_id"
    t.index ["project_id"], name: "index_external_subscriptions_on_project_id"
  end

我在 Rails 5.0.x

编辑

我的真实模型比这更复杂。在这里,我只是展示关系以便于讨论,但我不能将subscriptionsexternal_subscriptions 合并到同一个模型中。

【问题讨论】:

  • SubscriptionExternalSubscription 的表结构相同。您可以使用单表继承 (edgeguides.rubyonrails.org/…) 或简单的 subscription_type 枚举字段来创建两个单独的 has_many 关系 subscriptionsexternal_subscriptions 以及 all_subscriptions
  • 可以使用 SQL VIEW 在两个表上执行 UNION ALL,然后 has_many through 该视图来实现
  • 表格是一样的。也许您只需要在订阅表上对该表进行判别(如布尔标志external),然后相应地更新您的逻辑。

标签: ruby-on-rails activerecord


【解决方案1】:

您的订阅和 external_subscriptions 中似乎不需要不同的信息(两个表具有相同的字段),我将只使用一个模型和表,并根据表中的新字段对订阅进行分类。通过使用适当的范围,您可以轻松访问所有关联的模型。

项目

class Project < ApplicationRecord
  has_many :subscriptions
  has_many :external_subscriptions, -> { external }, class_name: "Subscription"
  has_many :normal_subscriptions, -> { normal }, class_name: "Subscription"
  has_many :investors, through: :subscriptions
  has_many :external_investors, through: :external_subscriptions, :source => :investor
  has_many :normal_investors, through: :normal_subscriptions, :source => :investor

end

投资者

class Investor < ApplicationRecord
  has_many :subscriptions
  has_many :projects, through: : subscriptions
end

订阅

class Subscription < ApplicationRecord
  belongs_to :project
  belongs_to :investor
  enum type: [ :external, :normal ]

  scope :external, -> { where(type: :external) }
  scope :normal, -> { where(type: :normal) } 
end

然后您可以通过以下方式访问不同的项目投资者:

project = Project.first.
project.investors #all
project.external_investors #only external
project.normal_investors #only normal

【讨论】:

    【解决方案2】:

    感谢@MrYoshiji 的评论,我通过执行UNION 的SQL 视图解决了我的问题。

    这是 POC:https://github.com/yoones/rails-has_many-through-view

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-21
      • 1970-01-01
      • 2018-03-02
      • 1970-01-01
      相关资源
      最近更新 更多