忍者ブログ
ブログツール、2007/11/06作成
[1] [2] [3] [4] [5] [6
日付の選択は日時の選択で便利なビューヘルパのdate_selectとdatetime_select。

こんかいのハマりは初期値の設定。

特定の日付を指定するのは、第一引数にTimeデータを格納してやればいいのは知ってる。

しかし、

:prompt=>true
とか、
:include_blank=>true
とかやって、
初期値をこのプロンプトもしくはブランクに指定する方法がわからなくて探した…

ドキュメントに書いてないやん。

ググって見ても、初期値に特定の日付を指定する方法ばかり出てくる。
そんなんドキュメントにしっかり書いてあるやろが!!

と半ばキレながら試した見た。

そう、答えは簡単。
第一引数にnilを入れると。

でたよ~Rails得意の怪しかったら試してみろパターン。

というわけで、
解決しました…orz

PR
スケジューラー実装中のこと。

いろんな時間の予定を作って、特定の日時で切って取得するテストをする。
が、いざやってみると思ってない時間の予定が取得されてる。

おかしい。
とりあえずトレースしてみると、変な時間が入ってる。

午前0時に設定したはずが、午後3時とか。

境界テストのためにかなりの種類の予定を作っているので、
打ち間違いとか、間違って上書きとかの可能性を探って見るもだめ。

しょうがないので、
代入する時間、代入された時間をトレースしてみる。
代入するまではきちんとしてるのに、
オブジェクトに代入したとたんにおかしなことになってる。

あっれ~とか思いつつ、よくよく見てみると、
オブジェクトに代入した時間がJST +00:00になってるよ。

あ~、タイムロケールか~というわけで、タイムロケール設定をしていなかったんですねえ・・・

/config/environment.rbで、

config.time_zone = 'UTC'

config.time_zone = 'Tokyo'
に変更。

ちゃんとJST +09:00になりました。

ちゃんちゃん。

# ってダメダコレ
どうもDBへの入出力でおかしくなってるみたいで一度保存するとおかしな状態のままになってる。
しょうがないので、国内限定、ってことで
# config.time_zone = 'Tokyo'
とコメントアウトして対応。

orz
テーブル関連、has_oneとかhas_manyとかの関連を持っているとき、
ついついActiveRecord::Base.destroyとかをオーバーライドしたくなるときがある。

例:

class Parent < AciveRecord::Base
  has_many :chidren, :dependent=>:destroy

  # 属性:deleted, :boolean, :default=>false

end

class Child < AciveRecord::Base
  belongs_to :parent

  # 属性:deleted, :boolean, :default=>false

end

親も子も関連する全員が@deletedがtrueになったときだけ全削除の実行に入る、
という必要があったとする。

親の方は簡単で、エイリアスを作ってオーバーライドすればいい。

class Parent < AciveRecord::Base
  has_many :children, :dependent=>:destroy

  alias :destroy! :destroy
 
  def  check_destory
    if # 自分と子が全員削除可能
      return self.destory!
    end
  end

  def destroy
    self.deleted = true
    return self.save
    retrun check_destroy
  end

end

てな感じ。

問題は子のほう。

メソッドを普通にオーバーライドすると、親の方が削除された時に、
自動的にdestoryが呼ばれるので無限ループに陥ってしまう。

で、苦肉の策で考えたのがこの方法。

class Child < ActiveRecord::Base
  belongs_to :parent
 
  def destroy
    if caller.to_s =~ /.+has_many_dependent_destroy_for_message_receiver_relations.+/
      return super
    else
      self.deleted = true
      self.save
      return self.parent.check_destroy
    end
  end

end

callerで呼び出し元をたどって、
そこにhas_many_dependent_destroy_for_message_receiver_relations
(関連で:dependent=>:destroyを設定したときに、自動で呼び出される)
が含まれていたら親が削除された一連の動作で削除されていると判断して、
superを呼び出し、
それ以外はフラグを設定してそこで処理を握りつぶすという方法。

これなら、親が削除されたとき以外は削除されずにすむ。

callerのチェックとかってあんまりやりたくないんだけど、
この方法ってなんか問題あるのかしら。

滅多に使わない方法なのでちょっと悩む。
てか、なんかほかのもっといい方法があったら教えてほしい。


Railsアプリの最大のボトルネックとなりうるのはSQLとのやりとりである。
(Erbのレンダラーだという話は置いておく)

なので、Railsアプリでチューニングを行う時に真っ先に手をつけるのは
AR.find系のDBアクセスな部分になる。

ちょっとしたアプリを作れば、(普通にモデリングしている限り)複数の関連モデルができあがる。
この場合、チューニングのポイントはEagerLoadingにあり、
:hoge has_many :fugas
:fuga has_many :piyos
という関連にある場合は、一発ですべて取得できるこれは非常に便利。

Hoge.find(id, :include=>{:fugas=>:piyos})
とやれば、すべての関連をインスタンス化した状態で取得できる。

これは便利なので、結構めくら滅法使ってしまう。
関連先をループで回して表示したりすることがあるときは、これだけでかなりのチューニングになる。

さて、
ここからが本題。

たいていの場合、親になるモデルをインスタンス化するのはコントローラーの責務になる。
(当たり前ですが)

で、ちょっと複雑なことになったモデルクラスで、
parent.childlen.find(:all, :conditions=>[conditions])
みたいに取得するか、find_by_sqlするか、
それともループを回して配列にコピーするのがいいのか、
判断に迷うことがある。

こんな時、すでにEagerLoadingでロードされているのか、
はたまたロードされていないのか判断ができれば処理分岐で対応できる。

loaded?

これで判定がつく

parent has_many :children

だったとき、parentクラス上で、

if self.childlen.loaded?
  # ループで回す処理
else
  # DBアクセスを伴う処理
end

という感じ。

ちなみにテスト上では反映されないので注意。

ActiveRecordのfindメソッドの:conditionsオプションって、INを判ってくれない・・・

class Hoge
  :has_many :relations
  :has_many :fugas, :through=>relations

class Fuga
  :has_many :relations
  :has_many :hoges, :through=>relations

class Relation
  belongs_to :hoge
  belongs_to :fuga

とあったとして、Hogeインスタンス上から、
Fugaインスタンス配列を元にRelationインスタンス配列がほしかったとする。

ナチュラルに考えて試してみる
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas])
# nil

じゃあID配列で
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas.map{|f| f.id}])
# nil

それじゃあID配列をjoinしてカンマ区切りに
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas.map{|f| f.id}.join(',')])
# 最初の一つ目だけ

find_by~ならどうだ!!
self.relations.find_by_fuga_id(fugas.map{|f| f.id})
# 最初の一つ目だけ

まあ、当然ですよね・・・

結局find_by_sqlに頼ってしまうのね・・・

Relation.find_by_sql("SELECT * FROM relations WHERE hoge_id = #{self.id} AND fuga_id IN (#{fugas.map{|f| f.id}.join(',')})")

ActiveRecordって、ある一線を越えるとSQLを直接叩く方が早くてわかりやすいよね・・・
ちゃんちゃん
   次のページ
ついめ~じ
ブログ内検索
フリーエリア
サニーカメラ
Powered by Ninja Blog    template by Temp* factory    icon by MiniaureType

ブログ [PR]入院保険 通販