首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >具有相互依赖字段的Rails验证和初始化

具有相互依赖字段的Rails验证和初始化
EN

Stack Overflow用户
提问于 2018-08-23 22:16:35
回答 2查看 88关注 0票数 0

在用Rails编码了几年之后,我仍然不了解Rails的更高级概念。Rails的方式对于约定而不是配置是如此具体,但是当您进入企业编码时,所有规则都会消失,每个人都会用非标准的方式编写博客。下面是另一种情况:上下文验证,其中的上下文更加复杂(字段相互依赖)。基本上,在送货应用程序中,我需要用请求参数和一些计算值初始化AR对象。计算值依赖于请求参数,我不确定如何初始化和验证我的成员变量。

代码语言:javascript
运行
复制
table mailpieces
  mail_class
  weight
  sort_code
  t_indicator_id
end

class Mailpiece
  validates_presence_of: :mail_class
  validates_presence_of: :weight
  validates_presence_of: :sort_code
  validates_presence_of: :t_indicator_id

  def some_kind_of_initializer
    if mail_class == 'Priority'
      sort_code = '123'
    elsif mail_class == 'Express'
      if weight < 1
        sort_code = '456'
      else
        sort_code = '789'
      end
    end

    t_indicator = ndicator.find_by(name: 'blah')
    if sort_code = '456'
      t_indicator = Indicator.find_by(name: 'foobar')
    end
  end
end

mailpiece = Mailpiece.new(
  mail_class: params[:mail_class],
  weight: params[:weight])

#mailpiece.some_kind_of_initializer ?!

raise 'SomeError' if !mailpiece.valid?

some_kind_of_initializer应该是什么?

  1. ActiveRecord初始化的覆盖?这不是很好的练习。
  2. after_initialize。更多的铁轨。
  3. 在Mailpiece.new之后调用的自定义方法(例如mailpiece.some_kind_of_initializer)

无论哪种选择,问题在于sort_code和t_indicator的初始化取决于mail_class和权重是否有效。考虑到在输入mail_class之前,some_kind_of_initializer和write不应该为null,那么我应该如何编写验证呢?

  1. 将所有验证提取到json架构验证中。围绕mail_class和权重的更复杂的业务规则很难在json模式中编写。
  2. 将所有验证提取到某种类型的数据传输对象验证类中。远离铁轨做事的方式。我觉得自己是用.NET/Java编写的,我担心Rails会在以后(在验证、测试等方面)踢我的Java。
  3. 只有在初始化了sort_code和mail_class时才分配mail_class。这似乎是大多数Rails编写东西的方式,但这很困难。那么多如果/否则。这只是一个简单的例子,但是我的邮件中有引用,它们都执行这些类型的验证。如果这是正确的答案,那么我的直觉是,可能更容易将所有验证和所有初始化移到外部类/模块--也许接近选项2。

选项3代码重写

代码语言:javascript
运行
复制
def some_kind_of_initializer
  if mail_class && weight
    if (mail_class == 'Priority')
      sort_code = '123'
    elsif (mail_class == 'Express')
      if weight < 1
        sort_code = '456'
      else
        sort_code = '789'
      end
    end
  end

  if sort_code
    t_indicator = Indicator.find_by(name: 'blah')
    if sort_code = '456'
      t_indicator = Indicator.find_by(name: 'foobar')
    end
  end
end

我很想听听你对此的看法。在我看来,这是一个非常流行的AR用例,我不知道该怎么做。同样,这只是一个简单的案例。我的邮件模型有许多其他引用,它们对邮件对象属性具有依赖关系,并且具有与上面相同的样式的相互依赖关系。

EN

回答 2

Stack Overflow用户

发布于 2018-08-23 23:54:09

我不确定这是否有帮助,或者我正确地理解了您的问题,但是也许使用"case“而不是if/elsif/else,三元运算符、一些懒惰的赋值和一些额外的验证可以给您的代码一种更好的"rails”感觉。

使用延迟赋值,我的意思是您可以等待初始化sort_code和t_indicator,直到您实际需要使用它或保存它。

代码语言:javascript
运行
复制
def sort_code
  return self[:sort_code] if self[:sort_code].present?

  sort_code = case mail_class
              when 'Priority' then '123'
              when 'Express'
                weight < 1 ? '456' : '789' if weight
              end
  self[:sort_code] = sort_code
end

这样,sort_code在您需要第一次使用它之前就会被初始化,这样您就可以执行Mailpiece.new(mail_class:‘That’),而不用马上初始化sort_code。

当您需要保存对象并且从未调用mailpiece.sort_code时,问题就会出现。您可以有一个before_validation回调,它只是调用sort_code来初始化它,以防它还没有实现。

对于t_indicator也可以这样做。

我还会在您的验证中添加一些上下文。

代码语言:javascript
运行
复制
validates_presence_of :weight, if: Proc.new{|record| 'Express' == record.mail_class} #you could add some helper method "is_express?" so you can just do "if: :is_express?"
validates_presence_of :sort_code, if: Proc.new{|record| 'Priority' == record.mail_class or 'Express' == record.mail_class && record.weight.present?}

对不起,如果我没有回答你的问题,我甚至没有使用你的选择哈哈。

票数 0
EN

Stack Overflow用户

发布于 2018-08-24 04:18:34

不如:

代码语言:javascript
运行
复制
class Mailpiece
  validates_presence_of: :mail_class
  validates_presence_of: :weight
  validates_presence_of: :sort_code
  validates_presence_of: :t_indicator_id

  def default_mail_class
    'Priority'
  end

  def default_sort_code
    mail_class = mail_class || default_mail_class
    if mail_class == 'Priority'
      '123'
    elsif mail_class == 'Express'
      weight < 1 ? '456' : '789'
    end
  end
end

然后,当您需要计算出t_indicator时,只需按需要执行:

代码语言:javascript
运行
复制
def is_foobar_indicator?
  sort_code || default_sort_code == '456'
end

def t_indicator
    indicator_params = {name: is_foobar_indicator? ? 'foobar' : 'blah'}
    Indicator.find_by(indicator_params)
end

因此,您仍然可以在sort_code上进行验证(假设提供了用户),但在查看t_indicator时仍然使用默认值。我不知道模型的其余部分有多复杂,但我建议在需要它们之前不要查找值。此外,after_initialize风险很大,因为它在每次初始化之后都会运行您的代码,因此,如果您对N个项运行查询,但只在它们上查找,或者最终不使用您设置的默认值,那么after_initialize运行N次都是免费的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51995015

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档