【问题标题】:Request part of a Rails model from URL从 URL 请求 Rails 模型的一部分
【发布时间】:2019-07-31 10:30:01
【问题描述】:

我有一个名为 Tasks 的 Rails 模型,它只有一个属性:resource_id。这指的是另一个 Rails 网站上的 Resource(另一个模型)的 ID,该网站在不同的 URL 上运行,具有不同的数据库。

当您请求Resource(例如http://localhost:3020/resources/23.json)时,它会以JSON格式返回资源,例如:

{
  "title": "Hello",
  "description": "Lorem ipsum..."
}

我希望能够从我当前的 Rails 网站(带有Task 的网站)中引用请求的资源:

# returns a Task with `resource_id: 23`
@task = Task.find(1)

# refers to the Resource from the other Rails server
@task.resource.title

如何让 Rails 在从本地数据库中找到 Task 的同时获取 Resource

我尝试在task.rb 中创建一个resource 属性,当它被这样调用时请求Resource

require 'net/http'

class Task < ApplicationRecord
  def resource
    res = Net::HTTP.get(URI.parse('http://localhost:3020/resources/' + resource_id.to_s + '.json'))
    JSON.parse(res)
  end
end

但是,每次我都会调用task.resource,这非常慢。我可以只请求一次Resource 并将其存储在某种伪属性中(我不知道它叫什么)吗?

【问题讨论】:

    标签: ruby-on-rails activerecord net-http


    【解决方案1】:

    您需要为任务和资源添加关联:

    在task.rb中:

      has_many: :resources
    

    在resource.rb中:

      belongs_to: :task
    

    这些关联会自动将资源方法等方法添加到模型中:https://guides.rubyonrails.org/association_basics.html

    然后在你的控制器动作中,使用 :includes 连接两个表:

    @tasks = Task.includes(:resource)
    

    请看这里:https://apidock.com/rails/ActiveRecord/QueryMethods/includes

    这里有一个解释:https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

    【讨论】:

    • 对不起,也许我没有澄清。 TaskResource 位于不同的Rails 服务器上,因此带有Task 的服务器需要通过Net::HTTPResource 服务器发出请求
    • 如果每个任务都有不同的资源,我不明白如何避免在每次需要时调用远程资源,而不将资源拉入数据库。可能最简单的答案是在任务表上创建一个表或一个属性,并在每次调用时将资源写入其中。这样你就只需要远程调用一次资源,然后你就可以像我的回答一样使用常规的 ActiveRecord。
    • 我想这是个好主意,我会添加它作为答案。感谢您的建议!
    【解决方案2】:

    我是这样解决自己的问题的:

    首先,给Task添加一个字符串属性,名为resource_string

    task.rb

    require 'net/http'
    
    class Task < ApplicationRecord
      def resource
        JSON.parse(resource_string)
      end
    
      after_find do |task|
        task.resource_string = request_resource task
      end
    
      private
    
      def request_resource(task)
        Net::HTTP.get(URI.parse('http://localhost:3020/resources/' + task.resource_id.to_s + '.json'))
      end
    end
    

    然后您可以按如下方式访问资源:

    @task = Task.find(1)
    @task.resource['title']
    

    它只会发出一次请求,并在调用find方法后将其写入resource_string属性。

    【讨论】:

      【解决方案3】:

      查询外部数据库表的所有内容不是很有效。但是你可以为你的控制器添加一个缓存:

      例如,你可以在 redis 中使用缓存:

      require 'net/http'
      
      class Task < ApplicationRecord
        def resource
          res = $redis.fetch(:resource, expires_in: 1.hour) do
            Net::HTTP.get(URI.parse('http://localhost:3020/resources/' + resource_id.to_s + '.json'))
            JSON.parse(res)
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 2011-01-04
        • 2018-08-04
        • 1970-01-01
        • 1970-01-01
        • 2013-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多