我使用的是Rails 4.2.0和active_record-acts_as gem。
此gem模拟ActiveRecord模型的多表继承。
我的父模型名为附件,包含子模型规范和发布。
class Attachment < ActiveRecord::Base
actable
end
class Specification < ActiveRecord::Base
acts_as :attachment
end
class Release < ActiveRecord::Base
acts_as :attachment
end
我的附件模型包含字段name
,actable_id
,actable_type
(由acts_as gem使用)和标准回形针字段。
规范和发布有多个特定于其类型的字段(因此我认为它们不适合单表继承)。
我要做的是验证子模型上的name
而不是父模型,因为不同的规则适用于发布和规范。
在线验证似乎工作正常:
class Specification < ActiveRecord::Base
acts_as :attachment
validates :name, presence: true
end
但是当我尝试这样的事情时:
class Specification < ActiveRecord::Base
acts_as :attachment
validates :name, presence: true, uniqueness: { case_sensitive: true }
end
调用.valid时出现以下错误?
NoMethodError: undefined method `limit' for nil:NilClass
我写了一些适用于儿童模型的自定义验证,但我希望我不会这样做。
验证孩子的主要原因是因为我使用以下内容来获取更简洁的错误消息,具体取决于模型(规范,发布):
class Specification < ActiveRecord::Base
...
HUMANIZED_ATTRIBUTES = {
name: "Version"
}
def self.human_attribute_name(attr, options={})
HUMANIZED_ATTRIBUTES[attr.to_sym] || super
end
end
所以它返回的内容如下:
Version can't be blank.
代替Name can't be blank.
我还尝试使用以下内容验证附件(父级):
with_options if: Proc.new { |x| x.actable_type == "Specification" } do |s|
s.validates :name, presence: true, uniqueness: { case_sensitive: true }
end
但是我没有收到我想要的错误消息。 Name
代替Version
。
我可能大肆过分复杂化。有什么想法吗?
答案 0 :(得分:1)
只是总结一下上述评论中针对遭遇类似错误的讨论:
问题在于db中没有specifications.name列。
当您验证状态时,它仅检查属性,当您检查它在此列的数据库中搜索的唯一性时。
答案 1 :(得分:1)
正如Evgeny Petrov所做的那样,无法在子模型上验证父属性(可能可以使用自定义验证器完成)。
最后,我选择使用with_options if:
验证父模型,以定位某些actable_type
。
为了理清一些自定义错误消息,我将子类的HUMANIZED_ATTRIBUTES
哈希与父级的self.human_attribute_name
结合起来。
以下是一个例子:
class Attachment < ActiveRecord::Base
actable
before_validation :set_humanized_attributes
class << self; attr_reader :humanized_attributes end
@humanized_attributes = {}
with_options if: proc { |x| x.actable_type == 'Release' do |s|
s.validates :name, presence: true, uniqueness: { case_sensitive: false }
end
with_options if: proc { |x| x.actable_type == 'Specification' do |s|
s.validates :name, presence: true, uniqueness: { case_sensitive: true }
end
def self.human_attribute_name(attr, options = {})
humanized_attributes[attr.to_sym] || super
end
private
def set_humanized_attributes
@humanized_attributes = actable_type.constantize::HUMANIZED_ATTRIBUTES
end
end
class Specification < ActiveRecord::Base
acts_as :attachment
HUMANIZED_ATTRIBUTES = {
name: 'Version'
}
# child attribute validations here
end
class Release < ActiveRecord::Base
acts_as :attachment
HUMANIZED_ATTRIBUTES = {
name: 'Release'
}
# child attribute validations here
end
在rails控制台中:
> s = Specification.new
=> #<Specification id ... >
> s.valid?
=> false
> s.errors.full_messages
=> ["Version can't be blank"]
> r = Release.new
=> #<Release id ... >
> r.valid?
=> false
> r.errors.full_messages
=> ["Release can't be blank"]
需要检查一些边缘情况并使其更加健壮,但是现在这实现了我的要求。