【问题标题】:Rails: implement sharing blogs; has_many with STI: undefined method 'klass' for nil:NilClassRails:实现分享博客; has_many 与 STI:nil:NilClass 的未定义方法“klass”
【发布时间】:2015-09-06 01:50:29
【问题描述】:

刚开始在 Rails 4 项目中使用 STI。假设我有UserBlog,并且User 可以将他的非公开博客以editorsnormal viewers 的身份分享给其他一些用户。

type 列放在users 表中对我来说没有意义,因为在项目中,用户不仅与blogs 相关联,还与posts 之类的事物相关联。 (这里的博客更像是一个平台,帖子是文章。这里只是一个想法,可能是其他两件事。

所以我使用了另一个名为BlogUserAssociation 的模型来管理上述共享关系。基本上这个模型包含一个type 列,我继承了BlogEditorAssociationBlogViewerAssociation。 (名字有点笨拙。)第一个问题,这是处理“共享”情况的推荐方法吗?

有了上面的想法,我有:

# blog.rb
class Blog < ActiveRecord::Base
    ...
    has_many :blog_user_associations, dependent: :destroy
    has_many :editors, through: :blog_editor_associations, source: :user
    has_many :allowed_viewers, through: :blog_viewer_associations, source: :user # STI
    ...

# user.rb
class User < ActiveRecord::Base
    ...
    has_many :blog_user_associations, dependent: :destroy
    has_many :editable_blogs, through: :blog_editor_associations, source: :blog
    has_many :blogs_shared_for_view, through: :blog_viewer_associations, source: :blog
    ...

但是当我尝试使用 Rspec 进行测试时,

it { should have_many(:editors).through(:blog_editor_associations).source(:user) }

我收到了错误undefined method 'klass' for nil:NilClass

我相信这是因为我在User 中没有说has_many blog_editor_associations。但我想既然blog_editor_associations继承自blog_viewer_associations,我就不必再为子模型说has_many了。那么是否有理由不自动将has_many 绑定到子模型?

【问题讨论】:

    标签: ruby-on-rails activerecord rspec


    【解决方案1】:

    对于这种情况,STI 似乎有点矫枉过正。我更喜欢将属性添加到关联模型并使用范围来检索集合,具体取决于属性的值。例如,您可以将关联模型命名为BlogUser,并添加一个布尔值can_edit 列。 true 的值表示用户可以编辑关联的博客。

    那么模型看起来像这样:

    class Blog < ActiveRecord::Base
      has_many :blog_users
      has_many :users, through: :blog_users
      scope :editable, -> { where(blog_users: {can_edit: true}) }
    end
    
     class BlogUser < ActiveRecord::Base
       belongs_to :blog
       belongs_to :user
     end
    
     class User < ActiveRecord::Base
       has_many :blog_users
       has_many :blogs, through: :blog_users
       scope :editors, -> { where(blog_users: {can_edit: true}) }
     end
    

    所以user.blogs 检索与用户关联的所有博客,user.blogs.editable 检索用户可以编辑的所有博客。 blog.users 检索与博客关联的所有用户,blog.users.editors 检索所有可以编辑博客的用户。

    一些测试来证明:

    require 'rails_helper'
    
    RSpec.describe User, type: :model do
      describe "A user with no associated blogs" do
        let(:user) { User.create! }
        it "has no blogs" do
          expect(user.blogs.empty?).to be true
          expect(user.blogs.editable.empty?).to be true
        end
      end
    
      describe "A user with a non-editable blog association" do
        let(:user) { User.create! }
        let(:blog) { Blog.create! }
        before do
          user.blogs << blog
        end
        it "has one blog" do
          expect(user.blogs.count).to eq 1
        end
        it "has no editable blogs" do
          expect(user.blogs.editable.empty?).to be true
        end
      end
    
      describe "A user with an editable blog association" do
        let(:user) { User.create! }
        let(:blog) { Blog.create! }
        before do
          user.blog_users << BlogUser.new(blog: blog, user: user, can_edit: true)
        end
        it "has one blog" do
          expect(user.blogs.count).to eq 1
        end
        it "has one editable blog" do
          expect(user.blogs.editable.count).to eq 1
        end
      end
    end
    

    【讨论】:

    • 感谢您提醒范围。这确实使代码更简单。所以你不需要 BlogUser 和 Blog 之间的任何继承?作用域本质上是如何工作的?只是想看看是否有更多的建议。如果没有,我会选择你的
    • 所以你的意思是在 User 表中添加一个 can_edit 列?如果我在博客中也有 Post(更像是一个平台)并且用户可以共享其可编辑性怎么办?基本上不只是为了博客
    • can_edit 列添加到 BlogUser 表中。有关范围的更多详细信息,请查看 Rails 指南:guides.rubyonrails.org/association_basics.html 至于您对 Post 模型的问题,也许您可​​以将其变成另一个问题。
    猜你喜欢
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多