ActiveRecord:使用委托通过关联访问连接表属性

时间:2016-03-02 15:55:21

标签: ruby-on-rails activerecord associations

我的应用中有一个Post模型,它有一个posts属性,用于存储看起来像这样的JSON对象:

Post.last.posts = {
    twitter: 'This is a tweet',
    facebook: 'This is a facebook post'
}

用户创建一个Post,然后将其发送到多个平台。这种架构对原始应用程序设计有意义。现在,我想让用户能够隐藏他们对一个平台发布的帖子,而不会影响其他平台的帖子。因为帖子是数据库中的单个模型,所以我需要找到一种解决方法。我不确定这是否是最佳方法,但我决定在User模型和Post模型之间创建一个连接表。请注意,帖子是由不同的用户模型(Admin)创建的,而User只是查看帖子。

以下是我的模特:

class Post < ActiveRecord::Base
    has_many :post_users
    has_many :users, through: :post_users
end

class User < ActiveRecord::Base
    has_many :post_users
    has_many :posts, through: :post_users
end

class PostUser < ActiveRecord::Base
    belongs_to :post
    belongs_to :user
    delegate :hide_twitter, to: :post
    delegate :hide_facebook, to: :post

    def hide_twitter
        self.hide_twitter
    end
end

在我构建的视图中,每个Post都表示为一系列卡片 - 每个平台一张卡片。因此,如果帖子在Twitter和Facebook上,它将显示为两个单独的卡 - 每个平台一个。我想要做的是让User能够隐藏其中一张牌而不影响其他牌。由于Post属于许多用户,因此必须是联接表的属性(例如PostUser)。

我想知道的是,是否可以通过Post模型访问联接表的此属性?我想做类似以下的事情,但我不确定它是否可行,或者我是否在联接表中使用delegate采取了正确的方法。

current_user.posts.first.hide_twitter
=> false
current_user.posts.first.hide_facebook
=> true

当我使用上述delegate并尝试调用上面的代码行时,出现以下错误:

Post Load (1.4ms)  SELECT  "posts".* FROM "posts" INNER JOIN "post_users" ON "posts"."id" = "post_users"."post_id" WHERE "post_users"."user_id" = $1  ORDER BY "posts"."id" ASC LIMIT 1  [["user_id", 90]]
NoMethodError: undefined method `hide_twitter' for #<Post:0x007fc27d383f50>
from /Users/ACIDSTEALTH/.gem/ruby/2.3.0/gems/activemodel-4.2.5.1/lib/active_model/attribute_methods.rb:433:in `method_missing'

我意识到我可以像this answer那样做一些非常迂回的事情,但希望能有更优雅/传统的东西。

1 个答案:

答案 0 :(得分:0)

如下:

  class Post < ActiveRecord::Base
    has_many :post_users
    has_many :users, through: :post_users
  end

  class User < ActiveRecord::Base
    has_many :post_users
    has_many :posts, through: :post_users
  end

  class PostUser < ActiveRecord::Base
    belongs_to :post
    belongs_to :user
  end

然后:

PostUser.where(post: current_user.posts.first).hide_twitter