【问题标题】:Limit number of objects in has_many association限制 has_many 关联中的对象数量
【发布时间】:2010-02-14 23:20:42
【问题描述】:

我有一张相册,里面有很多张照片。 counter_cache 设置更新相册表中的 photos_count 列。如何限制相册的照片数量?

【问题讨论】:

  • 插入前检查计数?
  • 可能不是最可爱的,但最安全的是@Marcel Jackwerth sulution,其他使用validates_associated 允许您使用parent.children.create 无限制地创建孩子

标签: ruby-on-rails


【解决方案1】:

就我而言,使用validates_length_of 就足够了:

class Album
  has_many :photos
  validates_length_of :photos, maximum: 10
end

class Photo
  belongs_to :album
  validates_associated :album
end

【讨论】:

  • 这是最干净的解决方案
  • 可以确认这确实很好。唯一的问题是,如果你走另一条路,只是开始创建一堆属于相册的“照片”,那是不能说的。您需要将 :validate => true 添加到 belongs_to,尽管我还没有测试以确保这将始终按预期工作。
  • 实际上belongs_to :album, validate: true 根据api.rubyonrails.org/classes/ActiveRecord/Associations/… 检查父保存的关联,而不是子保存...所以您可以添加任意数量的子,这可能是个问题跨度>
  • 照片模型中不需要 validates_associated :album。它也可以在没有 validates_associated 的情况下工作。
【解决方案2】:

使用验证钩子:

class Album
  has_many :photos
  validate_on_create :photos_count_within_bounds

  private

  def photos_count_within_bounds
    return if photos.blank?
    errors.add("Too many photos") if photos.size > 10
  end
end

class Photo
  belongs_to :album
  validates_associated :album
end

【讨论】:

  • 感谢各位的建议。我已经让 Marcel 的代码工作了。
  • photos.size 是更好的选择 - blog.hasmanythrough.com/2008/2/27/count-length-size,来自类似问题 - stackoverflow.com/a/4836927/1396904
  • 按照你的建议修改,@andorov
  • 这个解决方案仍然有问题,您可以使用album.photos.create 添加任意数量的孩子,至少使用Rails 5.1.4,所以看起来@Marcel Jackwerth sulution 可能是最好的观点
  • @hurikhan77 我已经在没有_on_create 的情况下检查了它,但是从控制台调用parent.children.create 我仍然可以再创建一个child 并超过限制1(至少对于我的多态关联)...我想这里的问题是photos.size > 10 - 在验证的那一刻它是有效的,并且在新的photo 创建之后就变得无效......虽然它可能只是rails控制台问题,但我没有' t 检查它是否显示相同的行为,然后从应用程序代码调用 create
【解决方案3】:

向 Photo 模型添加自定义验证方法怎么样?

  LIMIT = 50

  validate_on_create do |record|
    record.validate_quota
  end

  def validate_quota
    return unless self.album
    if self.album.photos(:reload).count >= LIMIT
      errors.add(:base, :exceeded_quota)
    end
  end

【讨论】:

  • 感谢@Marcel Jackwerth!花一些时间在rails console 检查所有 3 个解决方案,发现您的解决方案是唯一真正防止创建“不需要的孩子”的解决方案))
  • 这个解决方案最适合我的类似场景。很高兴您在解决方案中包含 .reload。我最初省略了它,发现一些(但不是全部)场景会使用过时的计数,从而允许创建过多的子记录。
【解决方案4】:
ActiveRecord::Base.transaction do
  ActiveRecord::Base.connection.execute('LOCK TABLE pictures IN EXCLUSIVE MODE')
  if (@album.pictures.count < 10) 
    @album.pictures.create()
  end
end

我相信这是最正确的解决方案。它可以防止并发问题/竞争条件。

【讨论】:

  • 我会在其中添加一个reload 以确保 ActiveRecord 不会给您提供过时的结果。类似@album.pictures.reload.count &lt; 10
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多