【问题标题】:Rails - Polymorphic Favorites (user can favorite different models)Rails - 多态收藏夹(用户可以收藏不同的模型)
【发布时间】:2014-02-16 21:37:58
【问题描述】:

我们正在尝试添加多个收藏对象,用户可以在其中收藏许多不同的对象,但不知道如何使其工作。

这是最喜欢的型号:

class Favorite < ActiveRecord::Base
  # belongs_to :imageable, polymorphic: true
  belongs_to :user
  belongs_to :category
  belongs_to :business
  belongs_to :ad_channel
  belongs_to :location
  belongs_to :offer
end

用户模型:

class User < ActiveRecord::Base
  has_many :favorites, as: :favoritable
end

还有一个可以收藏的示例模型:

class Category < ActiveRecord::Base
  has_many :sub_categories
  has_many :ad_channels
  has_many :offers
  belongs_to :favoritable, polymorphic: true
end

我不确定这是否设置正确,所以这将是我们需要一些反馈的第一件事。

其次,我们如何为用户“收藏”某些东西?

这是我们迄今为止尝试的失败:

@user.favorites << Category.find(1)

编辑:这还需要一个收藏夹数据库表来记录事情吗?这对我们来说是一个相当新的概念。

【问题讨论】:

    标签: ruby-on-rails rails-activerecord polymorphic-associations activemodel


    【解决方案1】:

    模型关系

    您的Favorite 模型如下所示:

    class Favorite < ActiveRecord::Base
      belongs_to :favoritable, polymorphic: true
      belongs_to :user, inverse_of: :favorites
    end
    

    然后,您的User 模型将如下所示:

    class User < ActiveRecord::Base
      has_many :favorites, inverse_of: :user
    end
    

    那么,可以收藏的模型应该是这样的:

    class Category < ActiveRecord::Base
      has_many :favorites, as: :favoritable
    end
    

    是的,您的数据库中需要一个favorites 表。

    收藏项目

    因此,这应该允许您执行以下操作:

    @user.favorites << Favorite.new(favoritabe: Category.find(1))  # add favorite for user
    

    请记住,您需要将Favorite 的实例添加到@user.favorites,而不是favoritable 模型的实例。 favoritable 模型是Favorite 实例上的一个属性。

    但是,实际上,在 Rails 中执行此操作的首选方式如下:

    @user.favorites.build(favoritable: Category.find(1))
    

    寻找某一类的最爱

    如果您只想查找某种类型的收藏夹,您可以执行以下操作:

    @user.favorites.where(favoritable_type: 'Category')  # get favorited categories for user
    Favorite.where(favoritable_type: 'Category')         # get all favorited categories
    

    如果您要经常这样做,我认为将作用域添加到多态模型是非常干净的:

    class Favorite < ActiveRecord::Base
      scope :categories, -> { where(favoritable_type: 'Category') }
    end
    

    这允许你做:

    @user.favorites.categories
    

    从上面得到的结果与@user.favorites.where(favoritable_type: 'Category') 相同。

    允许用户只收藏一个项目一次

    我猜您可能还希望允许用户只能收藏一个项目一次,这样您就不会在执行@user.favorites.categories 之类的操作时出现重复的类别。以下是在 Favorite 模型上设置的方法:

    class Favorite < ActiveRecord::Base
      belongs_to :favoritable, polymorphic: true
      belongs_to :user, inverse_of: :favorites
    
      validates :user_id, uniqueness: { 
        scope: [:favoritable_id, :favoritable_type],
        message: 'can only favorite an item once'
      }
    end
    

    这使得收藏夹必须具有user_idfavoritable_idfavoritable_type 的唯一组合。由于favoritable_idfavoritable_type 组合在一起得到收藏项,这相当于指定所有收藏项必须具有user_idfavoritable 的唯一组合。或者,用简单的英语来说,“一个用户只能收藏一次”。

    向数据库添加索引

    出于性能原因,当您具有多态关系时,您需要在_id_type 列上建立数据库索引。如果您使用带有多态选项的 Rails 生成器,我认为它会为您执行此操作。否则,您将不得不自己做。

    如果您不确定,请查看您的 db/schema.rb 文件。如果您在 favorites 表的架构之后有以下内容,那么您就大功告成了:

    add_index :favorites, :favoritable_id
    add_index :favorites, :favoritable_type
    

    否则,将这些行放入迁移中并运行那个坏男孩。

    当您使用它时,您应该确保您的所有外键也有索引。在此示例中,这将是 favorites 表上的 user_id 列。同样,如果您不确定,请检查您的架构文件。

    关于数据库索引的最后一件事:如果您添加上一节所述的唯一性约束,您应该向数据库添加唯一性索引。你可以这样做:

    add_index :favorites, [:favoritable_id, :favoritable_type], unique: true
    

    这将在数据库级别强制执行唯一性约束,如果您有多个应用服务器都使用单个数据库,这是必要的,并且通常是正确的做事方式。

    【讨论】:

    • 这很好用。 inverse_of: 到底是什么意思?
    • 在我看来,这实际上有点愚蠢,因为它应该自动发生,但inverse_of 基本上告诉 Rails 这两个关系是彼此相反的。这可以防止 Rails 在内存中创建重复记录。在这里阅读更多:guides.rubyonrails.org/…
    • 再次感谢您。还有一个问题,你会如何获取收藏的类别?
    • 任何用户收藏过的分类,还是某个用户收藏过的分类?
    • 非常感谢。我们非常感谢分享如此详细的信息。希望我能给你寄一些金子什么的
    猜你喜欢
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    相关资源
    最近更新 更多