Rails:用户有很多帖子,但只有一个帖子可以粘贴/固定

时间:2016-11-23 17:18:52

标签: ruby-on-rails ruby validation

用户应该可以固定帖子/创建粘贴帖子。但他应该只被允许只针一个帖子。因此,如果他决定创建一个新的固定帖子/别针另一个帖子,他必须“取消固定”旧帖子(并显示错误信息)。

我的用户和帖子模型之间有一个简单的关联,post模型有一个名为“pinned”的列,它是一个布尔值(true或false)。这就是我尝试过的:

模型/ user.rb

class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  validates_uniqueness_of :posts, if: :only_one_pinned_post

  def only_one_pinned_post
    if self.post.where(pinned: true).size == 1
      true
    else
      false
    end
  end
end

模型/ post.rb

class Post < ApplicationRecord
  belongs_to :user
end

我的问题

  • 如何让用户只能固定一个帖子?
  • 如果他已经有固定的帖子但想要创建另一个帖子,我在哪里可以输入错误消息?

4 个答案:

答案 0 :(得分:1)

由于您希望确保仅在固定帖子上,您可能希望将验证添加到Post模型中:

class Post < ActiveRecord::Base
  belongs_to :user

  scope :pinned,  -> { where(pinned:true) }
  scope :without, ->(id) { where.not(id: id) if id }

  validate :only_one_pinned_post_per_user

private

  def only_one_pinned_post_per_user
    if pinned? && user.posts.pinned.without(id).any?
      errors.add(:pinned, 'Another post ist already pinned')
    end
  end
end

我想知道(从可用性的角度来看)以不同的方式实现它是否更好:也许你不应该告诉用户他不能固定多个帖子。相反,您可以保存新固定的帖子并自动取消固定任何其他帖子。这可以通过后保存回调来完成:

class Post < ActiveRecord::Base
  belongs_to :user

  scope :pinned,  -> { where(pinned:true) }
  scope :without, ->(id) { where.not(id: id) if id }

  after_save :ensure_only_one_pinned_post

private

  def ensure_only_one_pinned_post
    user.posts.pinned.without(id).update_all(pinned: false) if pinned?
  end
end

without范围用于查找并且不计算具有给定id的帖子。我在这种情况下使用它来确保在查找其他已经固定的帖子时找不到当前创建或更新的帖子。

答案 1 :(得分:1)

这是对你的问题的另一种看法(简单而干净):

class User < ApplicationRecord
  has_many :posts

  # Helper to get only one pinned post
  has_one :pinned_post, -> { where(pinned: true) }, class_name: "Post"
end


class Post < ApplicationRecord
  belongs_to :user

  after_save do
    if pinned?
      # Automatically unpin any other pinned posts
      user.posts.where("id != ?", id).update_all(pinned: false)
    end
  end
end

答案 2 :(得分:0)

我没有测试过,但我认为这应该是合适的。基本上,方法only_one_pinned_post在验证期间执行,并且在用户已经有固定帖子的情况下,向错误对象添加消息,这将阻止对象被保存。

class User < ApplicationRecord

  scope :pinned, -> { where(pinned: true) }

  has_many :posts, dependent: :destroy
  validate :only_one_pinned_post

  def only_one_pinned_post
    if pinned && (posts.pinned.pluck(:id) - [id]).any?
      errors.add(:pinned, 'Only one pinned post')
    end
  end
end

条件中的表达式posts.pinned.pluck(:id) - [id]返回固定帖子的ID列表,不包括我们要保存的帖子。这样我们就可以修改该用户的现有固定帖子。

答案 3 :(得分:0)

(user_id, pinned)复合字段的数据库中创建唯一索引。这是确定用户没有多个固定帖子的唯一途径。所有其他应用程序级别的验证都不可靠(尽管您可能/应该使用另外到唯一索引)