Rails:如何限制has_many关联中的项目数(来自Parent)

我想限制关联中的项目数。 我想确保用户没有超过X物品。 这个问题之前被问过 ,解决方案在孩子身上有逻辑:

提供的解决方案(针对类似问题):

class User  :destroy end class Thing  :create def thing_count_within_limit if self.user.things(:reload).count >= 5 errors.add(:base, "Exceeded thing limit") end end end 

硬编码的“5”是一个问题。 我的限制根据父级更改。 物品集合知道它相对于用户的限制。 在我们的例子中,管理员可以调整每个用户的限制(事物),因此用户必须限制其收集的东西。 我们可以让thing_count_within_limit请求其用户的限制:

 if self.user.things(:reload).count >= self.user.thing_limit 

但是,这是Thing的很多用户反省。 对用户的多次调用,特别是(:reload)对我来说是红色标志。

想要更合适的解决方案:

我认为has_many :things, :before_add => :limit_things会起作用,但我们必须提出一个例外来阻止链 。 这迫使我更新things_controller以处理exception,而不是if valid?的rails约定if valid? 或者if save

 class User has_many :things, :before_add => limit_things private def limit_things if things.size >= thing_limit fail "Limited to #{thing_limit} things") end end end 

这是Rails。 如果我必须努力工作,我可能做错了什么。

要做到这一点,我必须更新父模型,孩子的控制器,我不能遵循惯例? 我错过了什么吗? 我在滥用has_many, :before_add ? 我找了一个例子,使用:before_add,但找不到任何。

我考虑过将validation移到用户,但这只发生在用户保存/更新上。 我没有看到使用它来阻止添加Thing的方法。

我更喜欢Rails 3的解决方案(如果这个问题很重要)。

因此,如果您希望为每个用户设置不同的限制,您可以将things_limit:integer添加到User并执行

 class User has_many :things validates_each :things do |user, attr, value| user.errors.add attr, "too much things for user" if user.things.size > user.things_limit end end class Thing belongs_to :user validates_associated :user, :message => "You have already too much things." end 

使用此代码,您无法将user.things_limit更新为低于他已经获得的所有内容的数字,当然它限制用户通过他的user.things_limit创建内容。

应用示例Rails 4:

https://github.com/senayar/user_things_limit

保存完成后,validation当前计数会导致计数大于限制。 我发现阻止创建发生的唯一方法是在创建之前validation事物的数量是否小于限制。

这并不是说对User模型中的计数进行validation是没有用的,但这样做并不会阻止User.things.create ,因为用户的count集合在新的Thing对象之前有效 。保存后保存,然后变为无效。

 class User has_many :things end class Thing belongs_to :user validate :on => :create do if user && user.things.length >= thing_limit errors.add(:user, :too_many_things) end end end 

尝试这个,就像字符串:

 class User < ActiveRecord::Base has_many :things, :dependent => :destroy validates :things, length: {maximum: 4} end 

我以为我会在这里说话。 似乎大多数答案在竞争条件下都失败了。 我正在尝试限制可以在我们的应用中以特定价格点注册的用户数量。 检查Rails中的限制意味着10个同时注册可以通过,即使它超过了我试图设置的限制。

例如,假设我想将注册限制在不超过10个。假设我已经注册了5个用户。 我们还要说有6个新用户尝试同时注册。 在6个不同的线程中,Rails读取剩余的插槽数,并得到答案5 。 这通过了validation。 然后Rails允许所有注册通过,我有11个注册。 :/

这是我解决这个问题的方法:

 def reserve_slot(price_point) num_updated = PricePoint.where(id: price_point.id) .where('num_remaining <= max_enrollments') .update_all('num_remaining = num_remaining + 1') if num_updated == 0 raise ActiveRecord::Rollback end end 

使用这种方法,即使应用程序处于负载状态,我也绝不会允许比max_enrollments更多的注册。 这是因为validation和增量是在单个primefaces数据库操作中完成的。 另请注意,我总是在事务中调用此方法,因此它会在失败时回滚。

您如何使用accepts_nested_attributes_for进行调查?

accepts_nested_attributes_for:things,:limit => 5

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

也就是说,我认为accepts_nested_attributes_for似乎只适用于某些类型的情况。 例如,如果您正在创建命令行API,我认为这是一个非常糟糕的解决方案。 但是,如果你有一个嵌套的表单,它运行得很好(大部分时间)。

您可以尝试validates_length_ofvalidates_associated

 class Client < ActiveRecord::Base has_many :orders validates :orders, :length => { :maximum => 3 } end class Order < ActiveRecord::Base belongs_to :client validates_associated :client end 

快速测试表明valid? 方法按预期工作,但它不会阻止您添加新对象。

Rails 4 ,也许早期版本可以简单地validationcounter_cache的值。

 class User has_many :things validates :things_count, numericality: { less_than: 5 } end class Thing belongs_to :user, counter_cache: true validates_associated :user end 

请注意我已经使用:less_than因为:less_than_or_equal_to将允许things_count6因为它在计数器缓存更新后被validation。

如果要基于每个用户设置限制,可以创建things_limit列以与您设置的限制值进行动态比较。

 validates :things_count, numericality: { less_than: :things_limit } 

你应该试试这个。

 class Thing  :create def thing_count user = User.find(id) errors.add(:base, "Exceeded thing limit") if user.things.count >= 5 end end