Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何将逻辑从控制器移动到模型中?_Ruby On Rails_Ruby On Rails 4 - Fatal编程技术网

Ruby on rails 如何将逻辑从控制器移动到模型中?

Ruby on rails 如何将逻辑从控制器移动到模型中?,ruby-on-rails,ruby-on-rails-4,Ruby On Rails,Ruby On Rails 4,一个池有许多地址。要基于提交的范围创建多个地址记录 我的控制器中有以下逻辑: def create @pool = Pool.find(params[:pool_id]) unless address_params[:ipv4_range_start].blank? || address_params[:ipv4_range_stop].blank? (address_params[:ipv4_range_start]..address_params[:ipv4

一个池有许多地址。要基于提交的范围创建多个地址记录

我的控制器中有以下逻辑:

  def create
    @pool = Pool.find(params[:pool_id])

    unless address_params[:ipv4_range_start].blank? || address_params[:ipv4_range_stop].blank?
      (address_params[:ipv4_range_start]..address_params[:ipv4_range_stop]).each do |octet|
        params[:address][:ipv4_octet3] = octet
        @address =  @pool.addresses.build(address_params)
        if !@address.save
           render 'new'
        end
      end
      redirect_to pool_path(@pool), notice: "Address range created."

    else #something was missing
      @address = @pool.addresses.build(address_params)
      @address.errors.add_on_blank(:ipv4_range_start)
      @address.errors.add_on_blank(:ipv4_range_stop)
      render 'new'
    end
  end
# AddressesController
def create
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # ...
end
def create
  # Our code from before
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # The new code
  begin
    @pool.addresses.create_from_range!(address_params)
  rescue ActiveRecord::ActiveRecordError
    flash[:error] = "Address range not created!"
    render 'new' and return
  end

  redirect_to @pool, notice: "Address range created."
end
想知道我该如何将其移动到地址模型中?对于控制器来说似乎太多了,但我不知道如何迭代提交的范围,并从地址模型本身构建和保存每个地址

谢谢你的建议


吉姆

我认为你的直觉是正确的,我们可以在模型中引入很多

免责声明:此代码未经测试;将其复制并直接粘贴到代码中可能会以眼泪告终

因此,我们需要处理您的逻辑的两个部分。第一个是确保提供了
:ipv4\u range\u start
\u stop
。为此,我们可以使用验证。由于您似乎不希望所有地址都需要这些属性,因此我们可以使用
:on
选项来提供验证上下文。():

expndtw-1::require\u range上的
部分意味着此验证通常不会运行,它只会在我们告诉ActiveRecord使用
:require\u range
上下文时运行

现在我们可以在控制器中执行此操作:

  def create
    @pool = Pool.find(params[:pool_id])

    unless address_params[:ipv4_range_start].blank? || address_params[:ipv4_range_stop].blank?
      (address_params[:ipv4_range_start]..address_params[:ipv4_range_stop]).each do |octet|
        params[:address][:ipv4_octet3] = octet
        @address =  @pool.addresses.build(address_params)
        if !@address.save
           render 'new'
        end
      end
      redirect_to pool_path(@pool), notice: "Address range created."

    else #something was missing
      @address = @pool.addresses.build(address_params)
      @address.errors.add_on_blank(:ipv4_range_start)
      @address.errors.add_on_blank(:ipv4_range_stop)
      render 'new'
    end
  end
# AddressesController
def create
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # ...
end
def create
  # Our code from before
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # The new code
  begin
    @pool.addresses.create_from_range!(address_params)
  rescue ActiveRecord::ActiveRecordError
    flash[:error] = "Address range not created!"
    render 'new' and return
  end

  redirect_to @pool, notice: "Address range created."
end
这与
else
块中的代码实现了相同的功能,但真正的逻辑在模型中,Rails为我们填充
errors
对象

现在我们已经解决了这个问题,我们可以处理创建对象的问题了。为此,我们可以在Address中编写一个类方法。Rails模型中类方法的优点是它们在关联集合中自动可用,因此,例如,如果我们定义
Address.foo
类方法,我们可以免费获得
@pool.addresses.foo
。下面是一个将创建地址数组的类方法:

# Address model
def self.create_from_range!(attrs)
  start = attrs.fetch(:ipv4_range_start)
  stop = attrs.fetch(:ipv4_range_stop)

  self.transaction do
    (start..stop).map do |octet|
      self.create!(attrs.merge ipv4_octet3: octet)
    end
  end
end
这与
else
块几乎相同,只是稍微干净了一点。我们做
self.create在事务中,因此如果任何
创建s失败,它们将全部回滚。我们在
映射
块中执行此操作,而不是在
每个
块中执行此操作,因此,假设没有错误发生,该方法将返回所创建对象的数组

现在我们只需要在控制器中使用它:

  def create
    @pool = Pool.find(params[:pool_id])

    unless address_params[:ipv4_range_start].blank? || address_params[:ipv4_range_stop].blank?
      (address_params[:ipv4_range_start]..address_params[:ipv4_range_stop]).each do |octet|
        params[:address][:ipv4_octet3] = octet
        @address =  @pool.addresses.build(address_params)
        if !@address.save
           render 'new'
        end
      end
      redirect_to pool_path(@pool), notice: "Address range created."

    else #something was missing
      @address = @pool.addresses.build(address_params)
      @address.errors.add_on_blank(:ipv4_range_start)
      @address.errors.add_on_blank(:ipv4_range_stop)
      render 'new'
    end
  end
# AddressesController
def create
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # ...
end
def create
  # Our code from before
  @pool = Pool.find(params[:pool_id])

  @address = @pool.addresses.build(address_params)
  if @address.invalid?(:require_range)
    render 'new' and return
  end

  # The new code
  begin
    @pool.addresses.create_from_range!(address_params)
  rescue ActiveRecord::ActiveRecordError
    flash[:error] = "Address range not created!"
    render 'new' and return
  end

  redirect_to @pool, notice: "Address range created."
end
如您所见,我们使用了
@pool.addresses.create\u from\u range,因此将为我们填写关联(
地址#池id
)。如果需要,我们可以将返回的数组分配给实例变量,并在视图中显示创建的记录

这应该就是你所需要的了


另外,值得注意的是Ruby有一个很好的内置类,而且因为IP地址只是一个数字(例如,
3338456716
198.252.206.140
的十进制形式),所以可以将每个IP地址存储为一个4字节的整数,而不是四个单独的列。大多数数据库都有处理IP地址的有用内置函数(PostgreSQL实际上有一个内置的
inet
列类型)。然而,这实际上取决于您的用例,在这一点上,这样的优化可能还为时过早。干杯

这条线的目的是什么<代码>参数[:地址][:ipv4_octet3]=octet
因为它在循环中,所以它的最终值将只是上一次迭代中的
octet
的值。这是预期的行为吗?如果您可以编辑您的答案,以包括
参数
地址参数
的预期内容,以及应保存的地址对象的属性,这将非常有用。
地址参数
包含允许的参数
:ipv4范围_开始/停止,:ipv4_八位字节0,:ipv4_八位字节1,和:ipv4_octet2
:ipv4\u octet3
值是从范围中获得的,但由于它不是由表单提供的,因此我必须通过
参数[:address][:ipv4\u octet3]=octet
行手动插入它。
ipv4\u范围\u开始/停止
值是虚拟属性,不保存。希望这能有所帮助。非常感谢您给出如此完整的答案!这不仅解决了问题,而且我还学到了关于Rails的一些新的有用的东西。乔丹,你真是慷慨善良!很高兴它有帮助!另外,我做了一个小改动,就是从地址范围中删除
Address.create\u。这是不必要的,因为代码从不修改
attrs
merge
在执行合并之前复制散列;原始散列不变)。我还将
替换为@address.valid?(…)
,而不是
if@address.invalid?(…)
,只是因为它读起来容易一点。@Jordan,只是一个小建议。这里的Pool.find(params[:Pool_id])如果我们使用find方法,那么如果对象不存在,那么此行将抛出ActiveRecord::RecordNotFound。相反,我们可以使用Pool.where(id:params[:Pool_id]。在这种情况下,如果找不到记录,那么将返回nil而不是抛出异常。请分享您的想法:)@Ajay如果
@Pool
nil
,那么下一行将在
@Pool.addresses
上引发一个NoMethodError,因此,无论哪种方式,我们都会得到一个例外。你是对的,这应该以某种方式处理,但适当的方式取决于OP想要的行为。可能没有
池id
的地址仍然有效(例如,可能有另一个视图用于创建独立于池的地址),在这种情况下,需要添加逻辑来说明这一点。