从 URL 请求 Rails 模型的一部分

Request part of a Rails model from URL

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

当您请求 Resource(例如 http://localhost:3020/resources/23.json)时,它 returns 资源采用 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 并将其存储在本地某种伪属性中吗(我不知道它叫什么)?

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

在 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

以下是我如何解决自己的问题:

首先,向 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属性

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

例如,你可以使用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