2017-05-03 70 views
0

我需要通过多态关系检索多个对象,最好是单个查询。Rails 5:通过多态关系检索多个对象

用户模式我有这样的:

class User < ApplicationRecord 
    has_many :companies, through: :accounts 
    has_many :permissions 
    has_many :medias, class_name: 'Media::Medias', through: :permissions, 
      source: :permitable, source_type: 'Media::Medias' 
end 

然后我权限模型看起来是这样的:

class Permission < ApplicationRecord 
    belongs_to :permitable, polymorphic: true 
    belongs_to :user, optional: true 
end 

媒体::媒体模式

module Media 
    class Medias < ApplicationRecord 
    belongs_to :companies, optional: true, inverse_of: :medias 
    has_many :permissions, class_name: 'Permission', as: :permitable 
    has_many :users, through: :permissions 
    end 
end 

公司模式

class Company < ApplicationRecord 
    has_many :accounts, dependent: :destroy 
    has_many :users, through: :accounts 
    has_many :medias, class_name: 'Media::Medias', inverse_of: :company 
end 

如何返回ActiveRecord::Relation object所有媒体的current_user和公司每个媒体?重要的是,我必须通过Permission模式使用关系,我不应该通过Accounts得到公司。到目前为止,我已经建立了模型,因此我可以使用Current.user.medias获取所有媒体的current_user

谢谢!

更新

以下是我从几个版本的我试过了:

[4] pry(main)> user.medias.includes(:companies).map{|media| media.companies.id } 
    Media::Medias Load (1.4ms) SELECT "media_medias".* FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
NameError: uninitialized constant Media::Medias::Companies 
from /home/ubuntu/.rvm/gems/ruby-2.3.3/gems/activerecord-5.1.0/lib/active_record/inheritance.rb:166:in `compute_type' 
[5] pry(main)> user.medias.preload(:companies).select(['medias.id', 'medias.company.id']) 
    Media::Medias Load (1.3ms) SELECT medias.id, medias.company.id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
=> #<Media::Medias::ActiveRecord_AssociationRelation:0x33dd094> 
[6] pry(main)> user.medias.select(['medias.id', 'medias.company_id']) 
    Media::Medias Load (1.1ms) SELECT medias.id, medias.company_id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
=> #<Media::Medias::ActiveRecord_AssociationRelation:0x3413234> 

更新2

[4] pry(main)> puts user.medias.preload(:companies).select(['medias.id', 'medias.company.id']) 
    Media::Medias Load (1.4ms) SELECT medias.id, medias.company.id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "medias" 
LINE 1: SELECT medias.id, medias.company.id FROM "media_medias" INNE... 
      ^
: SELECT medias.id, medias.company.id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 
from /home/ubuntu/.rvm/gems/ruby-2.3.3/gems/activerecord-5.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:620:in `async_exec' 
[5] pry(main)> puts user.medias.select(['medias.id', 'medias.company_id']) 
    Media::Medias Load (1.2ms) SELECT medias.id, medias.company_id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "medias" 
LINE 1: SELECT medias.id, medias.company_id FROM "media_medias" INNE... 
      ^
: SELECT medias.id, medias.company_id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 
from /home/ubuntu/.rvm/gems/ruby-2.3.3/gems/activerecord-5.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:620:in `async_exec' 

更新3

[1] pry(main)> User.find(2).medias.preload(:companies).select(['medias.id', 'medias.company.id']) 
    User Load (21.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]] 
    Media::Medias Load (1.7ms) SELECT medias.id, medias.company.id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
=> #<Media::Medias::ActiveRecord_AssociationRelation:0x17688f0> 
[2] pry(main)> User.find(2).medias.select(['medias.id', 'medias.company_id']) 
    User Load (1.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]] 
    Media::Medias Load (1.8ms) SELECT medias.id, medias.company_id FROM "media_medias" INNER JOIN "permissions" ON "media_medias"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = $1 AND "permissions"."permitable_type" = $2 [["user_id", 2], ["permitable_type", "Media::Medias"]] 
=> #<Media::Medias::ActiveRecord_AssociationRelation:0x2155d90> 
+0

你什么的'current_user.medias.includes(:公司)'? –

+0

@ Md.FarhanMemon我在console中看到:'[2] pry(main)> user.medias.includes(:companies) Media :: Medias Load(1.6ms)SELECT“media_medias”。* FROM“media_medias”INNER JOIN“permissions”ON“media_medias”。“id”=“permissions”。“permitable_id”WHERE“permissions”。“user_id”= $ 1 AND“permissions”。“permitable_type”= $ 2 [[“user_id”,2] “媒体::媒体”]] =>#' – matiss

+0

@ Md.FarhanMemon它看起来没有给我想要的结果+我需要找回两个 - 媒体和公司。 – matiss

回答

1

为了解决这个问题,看起来像这个问题(或者至少是一个大问题)是模型名称和关联中复数/单数元素的混淆。一般情况下:

  1. 型号名称是单数。在这种情况下,“中”是单数,“媒体”是复数。
  2. 在模型中使用关联构造时,“has_many”通常会采用复数形式,而“belongs_to”将采用单数形式。所以,期待has_many :companiesbelongs_to :user
  3. 基础表名是复数。所以,“中”模型是基于“媒体”表。

这样一来,我创建了一个简单的Rails应用程序,它是上述代码的最小工作示例。由于“帐户”模型没有出现,我从公司模型中删除了相应的元素。我还添加了反向关联。

class User < ApplicationRecord 
    has_many :permissions, inverse_of: :user 
    has_many :media, class_name: 'Medium::Medium', through: :permissions, source: :permitable, source_type: 'Medium::Medium' 
end 

class Company < ApplicationRecord 
    has_many :media, class_name: 'Medium::Medium', inverse_of: :company 
end 

class Medium::Medium < ApplicationRecord 
    belongs_to :company, optional: true, inverse_of: :media 
    has_many :permissions, class_name: 'Permission', as: :permitable, inverse_of: :permitable 
    has_many :users, through: :permissions 
end 

class Permission < ApplicationRecord 
    belongs_to :permitable, polymorphic: true, inverse_of: :permissions 
    belongs_to :user, inverse_of: :permissions 
end 

与测试数据样本控制台会话:

2.2.7 :001 > Company.create(name: 'IBM') 
    (0.1ms) begin transaction 
    SQL (0.4ms) INSERT INTO "companies" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "IBM"], ["created_at", "2017-05-05 19:11:49.017155"], ["updated_at", "2017-05-05 19:11:49.017155"]] 
    (0.5ms) commit transaction 
=> #<Company id: 1, name: "IBM", created_at: "2017-05-05 19:11:49", updated_at: "2017-05-05 19:11:49"> 
2.2.7 :002 > User.create(name: 'Joe') 
    (0.0ms) begin transaction 
    SQL (0.8ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Joe"], ["created_at", "2017-05-05 19:11:59.401417"], ["updated_at", "2017-05-05 19:11:59.401417"]] 
    (2.3ms) commit transaction 
=> #<User id: 1, name: "Joe", created_at: "2017-05-05 19:11:59", updated_at: "2017-05-05 19:11:59"> 
2.2.7 :003 > Medium::Medium.create(name: 'TV') 
    (0.0ms) begin transaction 
    SQL (0.2ms) INSERT INTO "medium_media" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "TV"], ["created_at", "2017-05-05 19:12:11.453440"], ["updated_at", "2017-05-05 19:12:11.453440"]] 
    (2.0ms) commit transaction 
=> #<Medium::Medium id: 1, company_id: nil, name: "TV", created_at: "2017-05-05 19:12:11", updated_at: "2017-05-05 19:12:11"> 
2.2.7 :004 > Medium::Medium.create(name: 'Newspaper') 
    (0.1ms) begin transaction 
    SQL (0.3ms) INSERT INTO "medium_media" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Newspaper"], ["created_at", "2017-05-05 19:12:18.433671"], ["updated_at", "2017-05-05 19:12:18.433671"]] 
    (2.0ms) commit transaction 
=> #<Medium::Medium id: 2, company_id: nil, name: "Newspaper", created_at: "2017-05-05 19:12:18", updated_at: "2017-05-05 19:12:18"> 
2.2.7 :005 > user = User.first 
    User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] 
=> #<User id: 1, name: "Joe", created_at: "2017-05-05 19:11:59", updated_at: "2017-05-05 19:11:59"> 
2.2.7 :006 > user.permissions.create(permitable: Medium::Medium.first) 
    Medium::Medium Load (0.2ms) SELECT "medium_media".* FROM "medium_media" ORDER BY "medium_media"."id" ASC LIMIT ? [["LIMIT", 1]] 
    (0.0ms) begin transaction 
    SQL (0.2ms) INSERT INTO "permissions" ("permitable_type", "permitable_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["permitable_type", "Medium::Medium"], ["permitable_id", 1], ["user_id", 1], ["created_at", "2017-05-05 19:12:50.694383"], ["updated_at", "2017-05-05 19:12:50.694383"]] 
    (0.5ms) commit transaction 
=> #<Permission id: 1, permitable_type: "Medium::Medium", permitable_id: 1, user_id: 1, created_at: "2017-05-05 19:12:50", updated_at: "2017-05-05 19:12:50"> 
2.2.7 :007 > user = User.first 
    User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] 
=> #<User id: 1, name: "Joe", created_at: "2017-05-05 19:11:59", updated_at: "2017-05-05 19:11:59"> 
2.2.7 :008 > user.media 
    Medium::Medium Load (0.2ms) SELECT "medium_media".* FROM "medium_media" INNER JOIN "permissions" ON "medium_media"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = ? AND "permissions"."permitable_type" = ? LIMIT ? [["user_id", 1], ["permitable_type", "Medium::Medium"], ["LIMIT", 11]] 
=> #<ActiveRecord::Associations::CollectionProxy [#<Medium::Medium id: 1, company_id: nil, name: "TV", created_at: "2017-05-05 19:12:11", updated_at: "2017-05-05 19:12:11">]> 
2.2.7 :009 > user.media.includes(:company) 
    Medium::Medium Load (0.2ms) SELECT "medium_media".* FROM "medium_media" INNER JOIN "permissions" ON "medium_media"."id" = "permissions"."permitable_id" WHERE "permissions"."user_id" = ? AND "permissions"."permitable_type" = ? LIMIT ? [["user_id", 1], ["permitable_type", "Medium::Medium"], ["LIMIT", 11]] 
=> #<ActiveRecord::AssociationRelation [#<Medium::Medium id: 1, company_id: nil, name: "TV", created_at: "2017-05-05 19:12:11", updated_at: "2017-05-05 19:12:11">]> 

只要确保让您的型号名称正确,它应该工作。示例代码可以在这里下载:

https://github.com/mdchaney/so_43765182

+1

谢谢,这有助于!我根据您的建议和[Rails文档](http://guides.rubyonrails.org/active_record_basics.html#naming-conventions)修改了我的模型,表格和列。它工作得很好,看起来不错:) Rails社区最好的! – matiss

0

尝试在User类增加一个关联关系:

class User < ApplicationRecord 
    has_many :companies, through: :accounts 
    has_many :permissions 
    has_many :medias, class_name: 'Media::Medias', through: :permissions, 
     source: :permitable, source_type: 'Media::Medias' 
    has_many :media_companies, through: :medias, source: :companies 
end 

那么你应该能够做到: User.find(2).media_companies

既然你已经从媒体到公司belongs_to关联,那么你应该考虑使用在您的Media::Medias类中单数belongs_to :company

如果你这样做,那么你将修改在User类的关联是:

has_many :media_companies, through: :medias, source: :company 

如果你想保持它的复数,那么你将需要添加一个关联选项得到它工作(避免NameError: uninitialized constant Media::Medias::Companies错误):

module Media 
    class Medias < ApplicationRecord 
    belongs_to :companies, class_name: :Company, optional: true, inverse_of: :medias 
    ... 
    end 
end 

这将是更传统的,只是它切换到单数,但:

belongs_to :company, optional: true, inverse_of: :medias 
+0

谢谢!我把@MichaelChaney建议的:1)'companies'改为'company',2)'Media :: Medias'改为'Medium :: Media'。我现在收回公司。我该如何为用户获取媒体? – matiss

+0

我知道了 - 'User.find(2).media.includes(:company)'现在工作得很好:) – matiss