与多个模型的关系[有趣的轨道关系概念]

时间:2018-02-10 13:47:27

标签: ruby-on-rails activerecord orm model relationship

我有这个应用,其中模型User可以有多个“频道”。应用程序的渠道部分必须能够轻松扩展,每个部分都有自己的模型

我开始创建一个Channel模型,其belongs_to User关系与User has_many Channel相反。

我在考虑使用与此类似的API:

user = User.create(name: 'John')

user.channels.create(name: 'google', account_id: '1234') # should create a GoogleChannel::Google Model
user.channels.create(name: 'facebook', account_name: 'qwerty') # should create a FacebookChannel::Facebook Model

我正在玩Channel模型,每个频道模型都有一个专用模型,用于与该模型has_one关系的每个频道(google,facebook等)。< / p>

更新

我正在使用 mongoid 和rails

2 个答案:

答案 0 :(得分:1)

我不确定这是否有效。它使用STI。

第一种方法:单表继承

class User << ApplicationRecord
  has_many :channels
  delegate :google_channels, :facebook_channels, :twitter_channels, to: :channels
end

class Channel << ApplicationRecord
  belongs_to :user
  self.inheritance_column = :brand

  scope :google_channels,   -> { where(brand: 'Google')   } 
  scope :facebook_channels, -> { where(brand: 'Facebook') } 
  scope :twitter_channels,  -> { where(brand: 'Twitter') }

  def self.brands
    %w(Google Facebook Twitter)
  end 
end

class GoogleChannel << Channel; end
class FacebookChannel << Channel; end
class TwitterChannel << Channel; end

我认为你可以:

current_user.channels << GoogleChannel.new(name: "First Google Channel") 
current_user.channels << Facebook.new(name: "Facebook") 
current_user.channels << Twitter.new(name: "Tweety")
current_user.channels << GoogleChannel.new(name: "Second Google Channel") 

googs = current_user.google_channels
all = current_user.channels
# etc.

所有频道共享同一张桌子。如果您需要为每个不同的品牌提供不同的属性,这将不是最佳选择。

第二种方法:多态模型

如果每个型号(品牌)需要不同的表格,您可以使用多态方法(未经测试):

class User << ApplicationRecord
  has_many :channels
  has_many :google_channels, through: :channels, source: :branded_channel, source_type: 'GoogleChannel'
  has_many :facebook_channels, through: :channels, source: :branded_channel, source_type: 'FacebookChannel'
end

class Channel << ApplicationRecord
  belongs_to :user
  belongs_to :branded_channel, polymorphic: true
end

#This channel has its own table, and can have more attributes than Channel
class GoogleChannel << ApplicationRecord
  has_one :channel, as: :branded_channel
end

#This channel has its own table, and can have more attributes than Channel
class FacebookChannel << ApplicationRecord
  has_one :channel, as: :branded_channel
end

goog = GoogleChannel.create(all_google_channel_params)
face = GoogleChannel.create(all_facebook_channel_params)
current_user.channels << Channel.new(branded_channel: goog)
current_user.channels << Channel.new(branded_channel: face) 

答案 1 :(得分:1)

我假设你想创建一个User有很多频道的STI,所以在Rails 5你可以试试这个:

class User < ApplicationRecord
  has_many :user_channels
  has_many :channels, through: :user_channels
  has_many :facebook_channels, class_name: 'FacebookChannel', through: :user_channels
  has_many :google_channels, class_name: 'GoogleChannel', through: :user_channels
end

class UserChannel < ApplicationRecord
  # user_id
  # channel_id
  belongs_to :user
  belongs_to :channel
  belongs_to :facebook_channel, class_name: 'FacebookChannel', foreign_key: :channel_id, optional: true
  belongs_to :google_channel, class_name: 'GoogleChannel', foreign_key: :channel_id, optional: true
end

class Channel < ApplicationRecord
  # You need to have "type" column to be created for storing different chanels
  has_many :user_channels
  has_many :users, through: :user_channels
end

class FacebookChannel < Channel
  has_many :user_channels
  has_many :users, through: :user_channels
end

class GoogleChannel < Channel
  has_many :user_channels
  has_many :users, through: :user_channels
end

现在您可以执行current_user.facebook_channels.create(name: "My Facebook Channel")并使用FacebookChannel获取所有current_user.facebook_channels

基本上它与STI额外功能的常规has_many through关系一样 - 您将子模型名称存储在type模型的Channel列中。

<强>更新

对不起,我不知道我的建议不适用于MongoDB。也许您只需在channel_type表格中创建channels列,即可建立简单的channel belongs_to user关系,然后执行以下操作:

  1. current_user.channels.create(name: "My Channel Name", channel_type: "facebook")
  2. current_user.channels.where(channel_type: "facebook")
  3. 您可以简单地current_user.channels为用户提供所有渠道,然后如果您需要,您可以根据channel_type值对每条记录执行所需操作。 您甚至可以在Channel model中创建一些范围:

    class Channel < ApplicationRecord
      scope :facebook_channels, -> { where(channel_type: "facebook") }
    end
    

    然后你可以做你想要的current_user.channels.facebook_channels

    如果使用枚举,

    channel_type列可以是字符串或整数。

    此外,如果您创建visit列(例如&#34;布尔&#34;),您可以执行current_user.channels.where(visit: true)或创建scope :visit_true, -> { where(visit: true) }并执行current_user.channels.visit_true之类的操作

    你说什么?