我有一个名为Tasks
的Rails模型,它只有一个属性:resource_id
。这是指另一个Rails网站上运行在具有不同数据库的不同URL上的Resource
(另一个模型)的ID。
当您请求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
如何在从本地数据库中找到Resource
的同时让Rails获得Task
?
我尝试在resource
中创建一个task.rb
属性,当它被调用时请求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
一次并将其存储在某种伪属性中(我不知道它叫什么)?
查询外部数据库表的所有内容不是很有效。但是您可以为控制器添加缓存:
例如,您可以使用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
以下是我解决自己问题的方法:
首先,将一个字符串属性添加到名为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
属性。
您需要向任务和资源添加关联:
在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