我想限制关联中的项目数。我想确保用户没有超过X物品。这个问题was asked before和解决方案在孩子身上有逻辑:
class User < ActiveRecord::Base
has_many :things, :dependent => :destroy
end
class Thing <ActiveRecord::Base
belongs_to :user
validate :thing_count_within_limit, :on => :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
可行,但我们必须向stop the chain提出异常。这迫使我更新things_controller来处理异常,而不是if valid?
或if save
的rails约定。
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
要做到这一点,我必须更新父模型,孩子的控制器,我不能遵循惯例?我错过了什么吗?我误导了has_many, :before_add
吗?我找了一个例子:before_add,但找不到任何。
我考虑过将验证移到用户,但这只发生在用户保存/更新上。我没有看到使用它来阻止添加Thing的方法。
我更喜欢Rails 3的解决方案(如果这个问题很重要)。
答案 0 :(得分:26)
因此,如果您想为每个用户设置不同的限制,可以将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:
答案 1 :(得分:6)
验证当前计数会导致计数在保存完成后变得大于限制。我发现阻止创建发生的唯一方法是在创建之前验证事物的数量是否小于限制。
这并不是说对用户模型中的计数进行验证是没有用的,但这样做并不会阻止调用User.things.create
,因为用户的计数集合是有效的直到保存新的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
答案 2 :(得分:2)
试试这个,就像字符串一样:
class User < ActiveRecord::Base
has_many :things, :dependent => :destroy
validates :things, length: {maximum: 4}
end
答案 3 :(得分:2)
我以为我会在这里发出声音。似乎大多数答案在竞争条件下都失败了。我试图限制在我们的应用中以特定价格点注册的用户数量。检查Rails中的限制意味着10个同时注册可以通过,即使它超过了我试图设置的限制。
例如,假设我想将注册限制为不超过10个。我们说我已经注册了5个用户。我们还要说有6个新用户同时尝试注册。在6个不同的线程中,Rails读取剩余的插槽数,并得到答案5
。这通过了验证。然后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
更多的注册,即使应用程序处于负载状态。这是因为验证和增量是在单个原子数据库操作中完成的。另请注意,我总是在事务中调用此方法,因此它会在失败时回滚。
答案 4 :(得分:0)
您如何使用accepts_nested_attributes_for进行调查?
accepts_nested_attributes_for:things,:limit =&gt; 5
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
那就是说,我认为accepts_nested_attributes_for似乎只适用于某些类型的情况。例如,如果您正在创建命令行API,我认为这是一个非常糟糕的解决方案。但是,如果你有一个嵌套的表单,它运行得很好(大部分时间)。
答案 5 :(得分:0)
您可以尝试validates_length_of
和validates_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
A quick test表明valid?
方法按预期工作,但它不会阻止您添加新对象。
答案 6 :(得分:0)
在Rails 4
中,也许是早期版本,您只需验证counter_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_count
为6
,因为在计数器缓存更新后验证它。{/ p >
如果您想为每个用户设置限制,可以创建一个things_limit
列,以便与您设置的限制值进行动态比较。
validates :things_count, numericality: { less_than: :things_limit }
答案 7 :(得分:-5)
你应该试试这个。
class Thing <ActiveRecord::Base
belongs_to :user
validate :thing_count, :on => :create
def thing_count
user = User.find(id)
errors.add(:base, "Exceeded thing limit") if user.things.count >= 5
end
end