kelkronsoの日記

2008-06-12scaffold + 検索

これある意味マルチポストなのかなぁ…。

scaffold + 検索 with Rails2.0

| 10:54 | scaffold + 検索 with Rails2.0 - kelkronsoの日記 を含むブックマーク はてなブックマーク - scaffold + 検索 with Rails2.0 - kelkronsoの日記

こんなの作ります。

f:id:kelkronso:20080612092150p:image:w350:h280

どっかで見たことある感じですね!すいません真似しました><

概要

検索がない一覧なんて一覧じゃない!ということで

scaffoldに検索機能をつけたものを作りたいと思います。


おまけとしてセレクトボックスも付いてます。

結果

長いので始めに結果を書いておきます。

実装約20分!

正直10分くらいでできると思ってたんですが、それなりに時間がかかってしまいました。


検索に関してはほんのさわり程度です、実装の方法は他にもあると思います。

むしろ僕にベストな方法を教えてくだs(ry


もくじ

セレクトボックス実装編

検索実装編

反省

前準備

RubyRailsDB

手順

とりあえずやったことをつらつらと書いて行きたいと思います。


何はともあれrailsコマンド

rails -d mysql(僕はmysqlで作りました。


メインとなるtodoモデル関連を作成

ruby script\generate scaffold todo name:string


セレクトボックスにぶちこむcategoryマスタのモデルを作成

ruby script\generate model category label:string


todoマイグレーションファイルに、categoryとの関連を書きます。

t.belongs_to :category

モデルにも書いておく

belongs_to :category


ここらで、rake db:migrateを実行し

画面を確認しておく。


いくらIDEを使っていようと、やはりスクリプト言語なので細かいチェックは欠かせません。

ちなみに僕は、categoryのスペルを間違っていてエラーもらいました・・・。


ここからセレクトボックス!

これではまだ画面はいつものままなので、セレクトボックスを追加します。

<%= f.text_field :name %>これは元々追加されてるはずなので、その下あたりに以下を追加

<%= f.collection_select(:category_id, Category.find(:all), :id, :label) %>


collection_selectの、第一引数はコントローラ側で受け取るときのキーになります

第二引数はコレクションを、

第三引数はvalueにバインドするフィールド、第四引数は表示するフィールドを入れます。


これでセレクトボックスが表示されます。

ですがこのままだと中身が空なので、


ruby script\generate migration add_category_data

として、テストデータ作成用のマイグレーションファイルを作成します。(コンソールで入れてもokです


upメソッドの中に、以下を追加します。

Category.create :label=>'Task', :id=>1

Category.create :label=>'Ploblem', :id=>2


rake db:migrateを実行するとセレクトボックスの中に値が入ってるいるはずです。


ついでに変更画面にも新規画面と同じコードを追加しておきます。


これでもうtodoとcategoryを関連付けたデータが作成可能です!

ここまで約10分!予想以上に時間かかったな・・・。


一覧画面でもcategoryを参照

index画面に以下を追加します。

<%=h todo.name %>(元からある

<%=h todo.category.label %>


あれ、categoryはjoinしてないよね?

ARは遅延読み込みをサポートしているので、このままでも動きます!

当然、先に読み込んでおくことも可能です。


これで、一覧画面でもcategoryを参照できるようになったはずです


バリデーション

todonameに対して、必須のバリデーションをかけておきます。todoモデルに以下を追加すればOK

validates_presence_of :name


行ごとに色を変える

index画面に以下を追記しておきます。

<span style="font-weight:bold;"><tr style="background-color:<%=cycle("red", "yellow")%>"></span>

cycleというのは、呼ばれる毎に引数の値を順番に表示してくれるメソッドです。


やっと検索!

検索フォームを作成する方法はいろいろありますが、今回はform_tagを使います。


index画面に以下を追加

<%form_tag :action=>"search" do%>
    <%=text_field_tag :keyword, @keyword%>
    <%=select_tag :category_id, options_from_collection_for_select(Category.find(:all), :id, :label, @category_id.to_i)%>
    <%=submit_tag "検索"%>
<%end%>

セレクトボックスの作成の仕方が先ほどと少し異なっています。

options_from_collection_for_selectというのは、"<option value="・・・">・・・</option>"を、指定したコレクションサイズの分だけ作ってくれます。


コントローラに以下を追加

  def search
    
    @todos = Todo.find(:all,
      :conditions=>["name like ? and category_id = ?", "%#{params[:keyword]}%", params[:category_id]]
    )    
    @keyword, @category_id = params[:keyword], params[:category_id]
    render :action=>:index
  end

text_field_tag :keywordに入力した値が、params[:keyword]によって取得できます。

@keyword, @category_id = params[:keyword], params[:category_id]で、値をインスタンス変数に入れなおしているのは、検索実行後にも検索条件を保持するためです。


これで検索ができるようになるはずです!

ここまで約20分!


反省

全件検索ができない

ActiveRecordで動的にクエリーを作成するのは意外と難しいです。ここでnamed_scopeの出番なわけですが、それはまた今度ということで・・・。


category_idに対して必須のバリデーションがかかっていない

実際は、以下のような感じで実装したのですが、毎回DBアクセスが走るのはよろしくないなぁ。

validates_inclusion_of :category_id,
    :in => Category.find(:all).map{|i| i.id}
全然DRYじゃない

セレクトボックスを作成する箇所をhelperに移動する、部分テンプレートを用いてeditとnewの重複を省く、などなど。

ElyzaElyza2011/09/08 07:36I thought finding this would be so aruodus but it's a breeze!

tuixddstuixdds2011/09/08 16:43w6qRSt <a href="http://hglzpjqktjul.com/">hglzpjqktjul</a>

clqdlbymclqdlbym2011/09/10 19:06Q5bpa4 <a href="http://avphjcuynisb.com/">avphjcuynisb</a>

jwznytjwznyt2011/09/12 19:18hi2R4I , [url=http://dxpsdklfycpr.com/]dxpsdklfycpr[/url], [link=http://kdumyutxhjcd.com/]kdumyutxhjcd[/link], http://suoqwdxeyiyk.com/

2008-06-11

知って得した便利文法

10:00 | 知って得した便利文法 - kelkronsoの日記 を含むブックマーク はてなブックマーク - 知って得した便利文法 - kelkronsoの日記

知って得した便利文法 on Ruby - kelkronsoの日記

他にもこんなのあるよ!というのがあればコメントでよろしくお願いします!


自己代入

a ||= 1         # a が偽か未定義ならば1を代入。

↓こんな感じで

#インスタンス変数modelを取得する、modelがnilの場合はインスタンス化したものを返す
def model
  @model ||= Model.new
end

多重代入

a, b, c = 1, 2, 3 #a=1, b=2, c=3

あまり横に長くなるようなら素直に縦に書いたほうがいいかもしれませんね。


setter

def method_name=(arg)
  @arg = arg
end

こんな感じで使える!

object.method_name = 'test'

C#のプロパティみたいな感じですね。


"!"を極力使わない

if式のthenは省略する。また、if !xのような場合は、 unless xに置き換える。ただし、unlessの場合、 elseは使用しない。また、条件が十分に簡単で、一行で書ける場合は、if/while修飾子を使用してもよい。

Rubyコーディング規約

ばりばり使ってたら怒られました・・・。

2008-06-08

はてなグループって、はてなダイアリーで書いた記事がグループに対してリンク貼られるー

といった感じの機能かと思っていたら、こっちはこっちで記事書かなきゃいけないのか!

というわけで自分の記事に対してリンク張ってみる


http://d.hatena.ne.jp/kelkronso/20080608/1212891488:title


Rails使いの方々からのコメント待ってます><


form_forについて分かったことまとめ

04:44 | form_forについて分かったことまとめ - kelkronsoの日記 を含むブックマーク はてなブックマーク - form_forについて分かったことまとめ - kelkronsoの日記

第一引数

インスタンス or 文字列 or シンボルを指定

(配列でもいけるっぽいけど、どんなときに使うんだろうか


アクション先で取得するハッシュのキーには (param[:key]こんな感じで

第一引数が文字列、シンボルならその文字列が

インスタンスならそのクラス名が使われる(と思われる


オプションいろいろ

methodの指定

アクション先の指定

  • :url=>(route_path @instance_name)
    url=>{:action=>"action_name", :id=>@instance_name}


scaffoldを作成したときにできるviewは、newもeditも、下記のようになっている

<% form_for(@article) do |f| %>

なのにアクション先がcreateかupdateかに分岐できるかというと

インスタンスのnew_record?を見て判断しているから


というわけで

ActiveRecordを継承していないクラスを引数に使うときに

new_record?メソッドで適切な値を返してやると、form_forをうまくだますことができる。

(けどここまでやるなら違う方法でやったほうがいいよなー。

BlogPet