【问题标题】:RoR converting a virtual attribute into two database attributesRoR 将一个虚拟属性转换为两个数据库属性
【发布时间】:2015-04-21 07:57:59
【问题描述】:

我目前无法找到一种对以下情况进行编码的好方法:

有一个Model叫做TcpService,它有两个属性port_from和port_to,都是整数。它还有一个名为portrange 的虚拟属性,它是一个字符串。 portrange 是属性port_from 和port_to 的字符串表示,所以portrange = "80 90" 应该产生port_from = 80、port_to = 90。我现在要做的是对creating 和updating 和TcpService-object 使用相同的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 失败,我希望它显示错误的用户输入以及错误,否则使用portrange 填充port_from 和port_to。在edit 上,我希望出现port_from 和port_to 的字符串表示,如果update 失败,我希望它显示错误的用户输入以及错误,否则使用@ 填充port_from 和port_to 987654347@.

模型看起来像这样,这对我来说似乎很乱。 有没有更好的方法让它达到我的需要?

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

感谢阅读

【问题讨论】:

    标签: ruby-on-rails validation formtastic


    【解决方案1】:

    在你的模型中

    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。

    【讨论】:

    • 抱歉 dping,实际上我想详细说明一下,但 SO 并没有让我正确编辑。在尝试创建/更新时,值会消失,以防它们不正确。这可能是因为我调用了 TcpService.new(tcp_service_params)。如果我喂它portrange = "X",它会调用portrange=("X"),它返回false,所以port_from和port_to保持未设置,导致表单显示一个空字符串而不是错误字符串。
    • 是的,当您使用错误格式时,它们仍然未设置。 return false unless str.match /^[0-9]{1,5}\ [0-9]{1,5}/ 可以验证您没有使用错误格式。我想我不太明白你到底想要什么。
    • 对不起,我的意思是,如果验证失败,表单应该显示错误值。我使用return @portrange if self.port_from.nil? || self.port_to.nil? 来实现这一目标,并在portrange=(str) 方法中使用@portrange = str。这有点难以解释;实际问题是,我使用的是虚拟属性,因此在获取数据库条目时,我需要为表单填充该属性,但如果验证失败,以后可能不会重新使用从数据库中获取的值。 (附带说明,最大 tcp 端口为 65,535)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 2011-07-23
    • 2014-11-12
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多