我正在使用Rails 5.我有以下模型
class MyObject < ActiveRecord::Base
...
belongs_to :distance_unit
...
def save_with_location
transaction do
address = LocationHelper.get_address(location)
if !self.address.nil? && !address.nil?
self.address.update_attributes(address.attributes.except("id", "created_at", "updated_at"))
elsif !address.nil?
address.race = self
address.save
end
# Save the object
save
end
end
通过一些狡猾的调试,我发现“save”方法会导致执行此查询......
DistanceUnit Load (0.3ms) SELECT "distance_units".* FROM "distance_units" WHERE "distance_units"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/models/my_object.rb:54:in `block in save_with_location'
每次调用上述方法时都会发生这种情况。这不是最佳的,因为我设置了我的DistanceUnit模型以获得缓存。下面是它的代码。如何让我的“保存”方法自动使用缓存而不是每次都执行此查询?
class DistanceUnit < ActiveRecord::Base
def self.cached_find_by_id(id)
Rails.cache.fetch("distanceunit-#{id}") do
puts "looking for id: #{id}"
find_by_id(id)
end
end
def self.cached_find_by_abbrev(abbrev)
Rails.cache.fetch("distanceunit-#{abbrev}") do
find_by_abbrev(abbrev)
end
end
def self.cached_all()
Rails.cache.fetch("distanceunit-all") do
all
end
end
end
答案 0 :(得分:3)
Rails 5在this change之后默认需要belongs_to
关联。这意味着相关记录必须在保存时存在于数据库中,否则验证将失败。有几种方法可以解决您的问题
1)在保存distance_unit
实例之前从缓存中手动设置MyObject
以防止从数据库中提取它:
def save_with_location
# ...
# Save the object
self.distance_unit = DistanceUnit.cached_find_by_id(self.distance_unit_id)
save
end
end
2)或选择退出此行为:
您可以将optional: true
传递给可以删除此验证检查的belongs_to
关联:
class MyObject < ApplicationRecord
# ...
belongs_to :distance_unit, optional: true
# ...
end
答案 1 :(得分:2)
老实说,我觉得设计不好
对于距离计算,建议使用另一种方法
道具:
距离计算逻辑的代码如下:
require 'active_record'
ActiveRecord::Base.logger = Logger.new STDOUT
class Address < ActiveRecord::Base
establish_connection adapter: 'sqlite3', database: 'foobar.db'
connection.create_table table_name, force: true do |t|
t.string :name
t.decimal :latitude, precision: 15, scale: 13
t.decimal :longitude, precision: 15, scale: 13
t.references :addressable, polymorphic: true, index: true
end
belongs_to :addressable, polymorphic: true
end
class Shop < ActiveRecord::Base
establish_connection adapter: 'sqlite3', database: 'foobar.db'
connection.create_table table_name, force: true do |t|
t.string :name
end
has_one :address, as: :addressable
end
class House < ActiveRecord::Base
establish_connection adapter: 'sqlite3', database: 'foobar.db'
connection.create_table table_name, force: true do |t|
t.string :name
end
has_one :address, as: :addressable
end
class Measure
def initialize address_from, address_to
@address_from = address_from
@address_to = address_to
end
def distance
# some calculation logic
lat_delta = @address_from.latitude - @address_to.latitude
lon_delta = @address_from.longitude - @address_to.longitude
lat_delta.abs + lon_delta.abs
end
def units
'meters'
end
end
shop_address = Address.new name: 'Middleberge FL 32068', latitude: 30.11, longitude: 32.11
house_address = Address.new name: 'Tucson AZ 85705-7598', latitude: 40.12, longitude: 42.12
shop = Shop.create! name: 'Food cort'
house = House.create! name: 'My home'
shop.update! address: shop_address
house.update! address: house_address
p 'Shop:', shop
p 'House:', house
measure = Measure.new shop_address, house_address
p "Distance between #{shop.name} (#{shop.address.name}) and #{house.name} (#{house.address.name}): #{measure.distance.to_s} #{measure.units}"
您可以使用:$ ruby path_to_file.rb
结果应该是:
"Shop:"
#<Shop id: 1, name: "Food cort">
"House:"
#<House id: 1, name: "My home">
"Distance between Food cort (Middleberge FL 32068) and My home (Tucson AZ 85705-7598): 20.02 meters"