我在哪里使用find_or_create_by方法?

时间:2014-05-07 22:21:31

标签: ruby-on-rails

使用Rails方法find_or_create_by时,它是属于模型还是控制器?我很难理解如何实际实现这个方法。

我希望我的rails应用程序接受来自用户的JSON消息。用户将数据发送回服务器,以便将其保存在数据库中。话虽这么说,我认为用户必须使用'POST'或'PATCH方法来存储或更新我服务器上的数据。当我查看我的路线时,创建动作使用'POST'方法。

我已经阅读了以下Rails文档,但它没有向我澄清任何内容。 http://guides.rubyonrails.org/active_record_querying.html#find-or-create-by

我会将find_or_create_by方法放在我的创建操作中吗?或者它属于其他地方?它在创建动作中似乎不正确......

class WifiNetworksController < ApplicationController
  def create
    @wifi_network = WifiNetwork.find_or_create_by(bssid: params[:bssid],
                                        ssid: params[:ssid],
                                        channel: params[:channel], etc...)
  end
end

最终我想:

  1. 用户通过JSON保存新网络(如果不存在)
  2. 如果某些属性已经改进(如信号强度),用户可以通过JSON更新现有网络
  3. 感谢您的时间!

    最后更新 - 感谢大家的好建议!我不得不采取一些每个人的建议来让它工作!以下是我最终做的事情..好像没有错误。

    def create
      respond_to do |format|
        if @wifi_network = WifiNetwork.find_by(bssid: wifi_network_params[:bssid])
          # Logic for checking whether to update the record or not
          @wifi_network.update_attributes(wifi_network_params) if @wifi_network.rssi < params[:rssi]
          format.json { render :nothing => true }
        else
          # Must be a new wifi network, create it
          @wifi_network = WifiNetwork.create(wifi_network_params)
          format.json { render :nothing => true }
        end
      end
    end
    

2 个答案:

答案 0 :(得分:3)

如果您使用强力参数,您可以在控制器中执行此操作:

def create
  @wifi_network = WifiNetwork.find_or_create_by(bssid: wifi_network_params[:bssid])
  @wifi_network.update_attributes(wifi_network_params)
end

然后当用户做出如下的卷曲时:

 curl -X POST localhost:3000/wifi_networks -d "wifi_network[bssid]=bssid1&wifi_network[ssid]=ssid1&wifi_network[channel]=channel1"

您的创建操作将通过它的bssid查找WifiNetwork并更新ssid和channel属性,或者如果它不存在,它将使用bssid param创建一个WifiNetwork,然后使用其余的更新新创建的记录的ATT。要小心,因为如果其他attrs的wifi_network_params为空,他们会将params更新为nil。

答案 1 :(得分:0)

我认为退一步并真正考虑应用程序的界面可能会很好。是否有任何特殊原因需要使用find_or_create_by并在一个控制器操作中执行所有操作?

为什么不通过单独创建&#39;来简化和遵守REST。并且&#39;更新&#39;您WifiNetworksController上的操作:

class WifiNetworksController < ApplicationController
  def create
    @wifi_network = WifiNetwork.new(wifi_network_params)

    if @wifi_network.save
      # success response
    else
      # failure response
    end
  end

 def update
   # params[:id] won't work here if the client sending the request doesn't know the id of the
   # wifi network, so replace it with the attribute you expect to be able to 
   # uniquely identify a WifiNetwork with.
   if @wifi_network = WifiNetwork.find(params[:id])
     # Logic for deciding whether to update or not
     @wifi_network.update_attributes(wifi_network_params) if @wifi_network.signal_strength < params[:signal_strength]
   else
     # wifi_network not found, respond accordingly
   end
 end

 private

 # strong_parameters for Rails 4
 def wifi_network_params
   params.require(:wifi_network).permit(:ssid, :channel,...)
 end
end

然后,您可以在WifiNetwork模型上进行验证,以确保某些属性是唯一的,以避免重复。

或者,如果您真的想要,可以将createupdate合并为一个操作,但create可能不是语义上的最佳名称。


编辑:在您的评论提供了一些背景信息之后,使用find_or_create_by可能没什么好处,因为您无法判断返回的记录是否已创建&#39;或者&#39;检索&#39;,这将允许您避免对其进行冗余更新操作。

假设bssid属性始终是唯一参数:

  def create
    if @wifi_network = WifiNetwork.find(params[:bssid])
      # Logic for checking whether to update the record or not
      @wifi_network.update_attributes(wifi_network_params) if @wifi_network.signal_strength < params[:signal_strength]
    else
      # Must be a new wifi network, create it
      @wifi_network = WifiNetwork.create(wifi_network_params)
    end
  end