RoR 将虚拟属性转换为两个数据库属性
RoR converting a virtual attribute into two database attributes
我目前无法找到一种很好的方法来编写以下情况的代码:
有一个名为 TcpService
的模型,它有两个属性,port_from
和 port_to
,都是整数。它还有一个名为 portrange
的虚拟属性,它是一个字符串。 portrange
是属性 port_from
和 port_to
的字符串表示形式,因此 portrange = "80 90"
应该产生 port_from = 80
、port_to = 90
。我现在要做的是对 creating
和 updating
对象使用相同的 Formtastic
形式。表格看起来很标准(HAML 代码):
= semantic_form_for @tcp_service do |f|
= f.inputs do
= f.input :portrange, as: :string, label: "Portrange"
-# calls @tcp_service.portrange to determine the shown value
= f.actions do
= f.action :submit, label: "Save"
问题是,我不知道有什么简单的方法可以使我想要的值出现在表单中。在 new
上,我希望该字段为空,如果 create
失败,我希望它显示错误的用户输入以及错误,否则使用 port_from
和 port_to
填充 portrange
。在 edit
上,我希望 port_from
和 port_to
的字符串表示形式出现,如果 update
失败,我希望它显示错误的用户输入以及错误,否则填充 port_from
和 port_to
使用 portrange
.
模型看起来像这样,我觉得很乱。
有没有更好的方法让它达到我的需要?
class TcpService < ActiveRecord::Base
# port_from, port_to: integer
attr_accessor :portrange
validate :portrange_to_ports # populates `port_from` and `port_to`
# using `portrange` AND adds errors
# raises exception if conversion fails
def self.string_to_ports(string)
... # do stuff
return port_from, port_to
end
# returns string representation of ports without touching self
def ports_to_string
... # do stuff
return string_representation
end
# is called every time portrange is set, namely during 'create' and 'update'
def portrange=(val)
return if val.nil?
@portrange = val
begin
self.port_from, self.port_to = TcpService.string_to_ports(val)
# catches conversion errors and makes errors of them
rescue StandardError => e
self.errors.add(:portrange, e.to_s())
end
end
# is called every time the form is rendered
def portrange
# if record is freshly loaded from DB, this is true
if self.port_from && self.port_to && @portrange.nil?
self.ports_to_string()
else
@portrange
end
end
private
# calls 'portrange=(val)' in order to add errors during validation
def portrange_to_ports
self.portrange = self.portrange
end
end
感谢阅读
在你的模型中
def portrange
return "" if self.port_from.nil? || self.port_to.nil?
"#{self.port_from} #{self.port_to}"
end
def portrange=(str)
return false unless str.match /^[0-9]{1,5}\ [0-9]{1,5}/
self.port_from = str.split(" ").first
self.port_to = str.split(" ").last
self.portrange
end
使用它,您应该能够在表单中使用 portrange
setter 和 getter。
我目前无法找到一种很好的方法来编写以下情况的代码:
有一个名为 TcpService
的模型,它有两个属性,port_from
和 port_to
,都是整数。它还有一个名为 portrange
的虚拟属性,它是一个字符串。 portrange
是属性 port_from
和 port_to
的字符串表示形式,因此 portrange = "80 90"
应该产生 port_from = 80
、port_to = 90
。我现在要做的是对 creating
和 updating
对象使用相同的 Formtastic
形式。表格看起来很标准(HAML 代码):
= semantic_form_for @tcp_service do |f|
= f.inputs do
= f.input :portrange, as: :string, label: "Portrange"
-# calls @tcp_service.portrange to determine the shown value
= f.actions do
= f.action :submit, label: "Save"
问题是,我不知道有什么简单的方法可以使我想要的值出现在表单中。在 new
上,我希望该字段为空,如果 create
失败,我希望它显示错误的用户输入以及错误,否则使用 port_from
和 port_to
填充 portrange
。在 edit
上,我希望 port_from
和 port_to
的字符串表示形式出现,如果 update
失败,我希望它显示错误的用户输入以及错误,否则填充 port_from
和 port_to
使用 portrange
.
模型看起来像这样,我觉得很乱。 有没有更好的方法让它达到我的需要?
class TcpService < ActiveRecord::Base
# port_from, port_to: integer
attr_accessor :portrange
validate :portrange_to_ports # populates `port_from` and `port_to`
# using `portrange` AND adds errors
# raises exception if conversion fails
def self.string_to_ports(string)
... # do stuff
return port_from, port_to
end
# returns string representation of ports without touching self
def ports_to_string
... # do stuff
return string_representation
end
# is called every time portrange is set, namely during 'create' and 'update'
def portrange=(val)
return if val.nil?
@portrange = val
begin
self.port_from, self.port_to = TcpService.string_to_ports(val)
# catches conversion errors and makes errors of them
rescue StandardError => e
self.errors.add(:portrange, e.to_s())
end
end
# is called every time the form is rendered
def portrange
# if record is freshly loaded from DB, this is true
if self.port_from && self.port_to && @portrange.nil?
self.ports_to_string()
else
@portrange
end
end
private
# calls 'portrange=(val)' in order to add errors during validation
def portrange_to_ports
self.portrange = self.portrange
end
end
感谢阅读
在你的模型中
def portrange
return "" if self.port_from.nil? || self.port_to.nil?
"#{self.port_from} #{self.port_to}"
end
def portrange=(str)
return false unless str.match /^[0-9]{1,5}\ [0-9]{1,5}/
self.port_from = str.split(" ").first
self.port_to = str.split(" ").last
self.portrange
end
使用它,您应该能够在表单中使用 portrange
setter 和 getter。