Web + Life Hack

〜True But Useless〜

【Rails】Railsのバグ?joinsを使ってハマるパターン



Railsで新しい検索条件(scopeなどでjoinsを使用)を追加した時にハマりました。
最近、ハマってしかない。。。。

前提条件

  • shopsテーブルには「company_id」が存在する。
  • companysテーブルには「shop_id」が存在する。
  • shop_profilesテーブルには「shop_id」は存在するが、「company_id」は存在しない。
  • shop_profilesテーブルには「g08m11_code」が存在する。
  • 検索対象は「g08m11_code」



こけるパターン

shops.rb

class Shop < ApplicationModel
 has_one :shop_profile, :dependent => :destroy
 scope :g08m11_code_is, lambda { |g08m11_code| joins(:shop_profile).where("shop_profiles.g08m11_code like ?", "%#{g08m11_code}%") unless g08m11_code.blank? }

 def self.find_by_search_form(form)
  g08m11_code_is(form.g08m11_code)
 end
end

shops_profiles.rb

class ShopProfile < ActiveRecord::Base
 belongs_to :shop
 scope :g08m11_code_is, lambda { |g08m11_code| where("g08m11_code like ?", "%#{g08m11_code}%") unless g08m11_code.blank? }
end


company.rb

class Company < ApplicationModel
 has_many :shops
 companies = companies.joins(:shops).merge(Shop.find_by_search_form(shop_search_form))
end

エラー内容

Completed 500 Internal Server Error in 937ms
ActiveRecord::ConfigurationError (Association named 'shop_profile' was not found; perhaps you misspelled it?)


shop_profileなんてないよ。タイプミスしてない?って怒られます。

試み

company側にshopsと同じ定義を書いてみたらどうか
Associationに関するエラーということから、
company側にShopと同じような記述を書いて見たらと考える。

class Company < ApplicationModel
 has_many :shops
 #追加部分
 has_one :shop_profile

end



結果

[2014-03-04 18:49:05.778375 #6976][DEBUG] -- : Mysql2::Error: Unknown column 'shop_profiles.company_id' in 'on clause': SELECT COUNT(*) FROM `companies` INNER JOIN `shops` ON `shops`.`company_id` = `companies`.`id` INNER JOIN `shop_profiles` ON `shop_profiles`.`company_id` = `companies`.`id` WHERE (companies.deleted_at IS NULL) AND (shops.deleted_at IS NULL) AND (shop_profiles.g08m11_code like '%111111%')
500 Internal Server Error
If you are the administrator of this website, then please read this web application's log file and/or the web server's log file to find out what went wrong.


最終的に落ち着いたコードは以下のとおり。

こけないパターン

shops.rb

class Shop < ApplicationModel
 has_one :shop_profile, :dependent => :destroy

 scope :g08m11_code_is, lambda { |g08m11_code| joins("join shop_profiles on shop_profiles.shop_id = shops.id").merge(ShopProfile.g08m11_code_is(g08m11_code)) unless g08m11_code.blank? }

 def self.find_by_search_form(form)
  g08m11_code_is(form.g08m11_code)
 end 
end

shops_profiles.rb

class ShopProfile < ActiveRecord::Base
 belongs_to :shop

 scope :g08m11_code_is, lambda { |g08m11_code| where("g08m11_code like ?", "%#{g08m11_code}%") unless g08m11_code.blank? }
end


company.rb

class Company < ApplicationModel
 has_many :shops
 companies = companies.joins(:shops).merge(Shop.find_by_search_form(shop_search_form))
end


joinsの指定方法を明記しないと

勝手にjoin先のidと結び付けようとします。
(この場合、company.idとshop_profiles.company_id(このカラムは存在しない))

単一の検索ロジックを複数のコントローラーで使用しかつ、
複数モデルとjoinsしているscopeなどの場合は
この記述で書いていこうと思いました。


個人的な写真:
良い天気
http://instagram.com/p/lFUwx5wYrk/