Rails: 通过join-table查询两步关系 + has many relation
Rails: query for 2-step relation through join-table + has many relation
我正在努力改进我的数据库方案。目前,我使用 has_many 直通结构,其中包含陆地 model/table 航运服务 model/table 和邮区 model/table,后者连接陆地和航运 tables。
我可以这样查询:
@shippingservices = @cart.available_shipping_services.includes(:lands, :postzones).where('postzones.land_id = ?', params[:id])
并获取给定土地(国家/地区)的所有运输服务。
但是这样我最终会在 postzone 中重复很多 table 并且维护起来非常困难。如果我可以在有许多土地的区域收集土地,而不是将航运服务连接到一个区域,那会容易得多。就像在这个方案中一样:
在这种情况下,我将如何查询给定土地的所有运输服务?
对于加入 table 我会做类似的事情:
@shippingservices = @cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
这不起作用,因为我得到:
Mysql::Error: Unknown column 'lands_zones_join.zone_id' in 'where clause': SELECT `shippingservices`.* FROM `shippingservices`
INNER JOIN `zones` ON `zones`.`id` = `shippingservices`.`zone_id`
INNER JOIN `lands_zones` ON `lands_zones`.`zone_id` = `zones`.`id`
INNER JOIN `lands` ON `lands`.`id` = `lands_zones`.`land_id`
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
WHERE (weightmin <= 50 AND weightmax >= 50 AND height >= 3 AND shippingservices.shippingcarrier = '1') AND (length >= 210 AND width >= 149) AND (lands_zones_join.zone_id = '2')
这可能吗?我怎样才能让它工作?
提前致谢!
更新
models/shippingservices.rb
class Shippingservice < ActiveRecord::Base
belongs_to :zone
has_many :lands, :through => :zone
end
app/models/zone.rb
class Zone < ActiveRecord::Base
has_and_belongs_to_many :lands, :join_table => "lands_zones"
has_many :shippingservices
end
app/models/landszone.rb
class LandsZone < ActiveRecord::Base
belongs_to :land
belongs_to :zone
end
app/models/land.rb
class Land < ActiveRecord::Base
has_and_belongs_to_many :zones, :join_table => "lands_zones"
has_many :shippingservices, :through => :zones
end
db/schema.rb
create_table "lands", :force => true do |t|
t.string "name"
end
create_table "lands_zones", :force => true do |t|
t.integer "land_id"
t.integer "zone_id"
end
add_index "lands_zones", ["land_id"], :name => "index_lands_zones_on_land_id"
add_index "lands_zones", ["zone_id"], :name => "index_lands_zones_on_zone_id"
create_table "shippingservices", :force => true do |t|
t.integer "zone_id"
t.string "name"
t.string "shippingcarrier"
end
create_table "zones", :force => true do |t|
t.string "name"
end
我通过删除时间戳等方式减少了它,但保留了所有必要的部分。
@shippingservices = @cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
where 子句中的 lands_zones_join
table 不正确。 Rails 将此视为派生的 table 名称并应用此处所示的两个额外连接:
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
这导致查询结果重复。
从 Shippingservice
到 Land
的关系已在模型和架构中正确设置。因此,我们可以直接加入 tables 并查询土地 table 本身的 id:
@cart.available_shipping_services.joins(:lands).where(lands: {id: params[:id]})
我正在努力改进我的数据库方案。目前,我使用 has_many 直通结构,其中包含陆地 model/table 航运服务 model/table 和邮区 model/table,后者连接陆地和航运 tables。
我可以这样查询:
@shippingservices = @cart.available_shipping_services.includes(:lands, :postzones).where('postzones.land_id = ?', params[:id])
并获取给定土地(国家/地区)的所有运输服务。
但是这样我最终会在 postzone 中重复很多 table 并且维护起来非常困难。如果我可以在有许多土地的区域收集土地,而不是将航运服务连接到一个区域,那会容易得多。就像在这个方案中一样:
在这种情况下,我将如何查询给定土地的所有运输服务?
对于加入 table 我会做类似的事情:
@shippingservices = @cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
这不起作用,因为我得到:
Mysql::Error: Unknown column 'lands_zones_join.zone_id' in 'where clause': SELECT `shippingservices`.* FROM `shippingservices`
INNER JOIN `zones` ON `zones`.`id` = `shippingservices`.`zone_id`
INNER JOIN `lands_zones` ON `lands_zones`.`zone_id` = `zones`.`id`
INNER JOIN `lands` ON `lands`.`id` = `lands_zones`.`land_id`
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
WHERE (weightmin <= 50 AND weightmax >= 50 AND height >= 3 AND shippingservices.shippingcarrier = '1') AND (length >= 210 AND width >= 149) AND (lands_zones_join.zone_id = '2')
这可能吗?我怎样才能让它工作?
提前致谢!
更新
models/shippingservices.rb
class Shippingservice < ActiveRecord::Base
belongs_to :zone
has_many :lands, :through => :zone
end
app/models/zone.rb
class Zone < ActiveRecord::Base
has_and_belongs_to_many :lands, :join_table => "lands_zones"
has_many :shippingservices
end
app/models/landszone.rb
class LandsZone < ActiveRecord::Base
belongs_to :land
belongs_to :zone
end
app/models/land.rb
class Land < ActiveRecord::Base
has_and_belongs_to_many :zones, :join_table => "lands_zones"
has_many :shippingservices, :through => :zones
end
db/schema.rb
create_table "lands", :force => true do |t|
t.string "name"
end
create_table "lands_zones", :force => true do |t|
t.integer "land_id"
t.integer "zone_id"
end
add_index "lands_zones", ["land_id"], :name => "index_lands_zones_on_land_id"
add_index "lands_zones", ["zone_id"], :name => "index_lands_zones_on_zone_id"
create_table "shippingservices", :force => true do |t|
t.integer "zone_id"
t.string "name"
t.string "shippingcarrier"
end
create_table "zones", :force => true do |t|
t.string "name"
end
我通过删除时间戳等方式减少了它,但保留了所有必要的部分。
@shippingservices = @cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
where 子句中的 lands_zones_join
table 不正确。 Rails 将此视为派生的 table 名称并应用此处所示的两个额外连接:
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
这导致查询结果重复。
从 Shippingservice
到 Land
的关系已在模型和架构中正确设置。因此,我们可以直接加入 tables 并查询土地 table 本身的 id:
@cart.available_shipping_services.joins(:lands).where(lands: {id: params[:id]})