WEB + DB PRESS 28号の【特集】Ruby on Rails入門に取り組む(5日目)

プチSNSを作って、ActiveRecordの使い方を学ぶ


Railsで一番便利(?)と言われている、ActiveRecordがメインの章らしい。

まずは取り組み前の目的意識を整理

ちょっと時間が経ってしまったので、前回のKPTを再確認。

前回のぷちKPT

  • KEEP【良かった事、続けていきたい事】
    • はてなDiaryに引き続き書いて、「見える化」とKPTができた。
    • 文章をわかりやすくシンプルまとめた。(前回からの改善)
    • 時間をかけずに行った。
  • PROBLEM【悪かったこと、改善したい事】
  • TRY【今後取り込みたいこと】
    • 予定より時間を生み出せた時には、関連項目の何かに狙いを絞って深く掘り下げて調べみる
    • 「名言」や「らしさ」や「繋がり」を意識して、Railsに関わりたい


よし思い出した、以前の改善点で対応した事は継続して習慣づける。

今日の目標は下記の2点にしよう。

  • 要点を掴んで、だらだら書かず実行していく
  • ActiveRecordに関して、新しい発見をする。(掘り下げを行う)

取り組み開始

実は、平日に帰宅中に第四章をざっと読んでおきました。

4章で使用するActiveRecordのポイント

  • 規約
  • リレーション
  • SQLとオプション値の関係
  • フック

っぽい感じ。SNSを作るというと難しいイメージでしたが、ページ数も4ページでわかりやすい。
仕様も簡単。「会員・コミュニティ・日記・ペット」という関係で、制約として会員はペットを1匹しか飼う事ができないというもの。

ER図も掲載されていたのですが、言葉で簡単にまとめた。

  • 『会員』多対多『コミュニティ』
  • 『会員』1対多『日記』
  • 『会員』1対1『ペット』


全て会員が起点になります。

会員が属するコミュニティ情報は中間テーブルに保存するみたい。
中間テーブルをRailsでどうやって扱う事ができるのかがすごく楽しみ。

DB関係とモデルを作成する

MySQLスキーマ(予めSQLを流してテーブルを作成しておく)

create table users (
id int unsigned not null auto_increment,
name varchar(20) not null,
password varchar(20) not null,
primary key (id)
);
create table communities (
id int unsigned not null auto_increment,
name varchar(20) not null,
description text not null,
primary key (id)
);
create table communities_users (
community_id int unsigned,
user_id int unsigned,
primary key (community_id, user_id)
);
create table topics (
id int unsigned not null auto_increment,
written_on date not null,
title varchar(255),
content text not null,
user_id int unsigned,
primary key (id),
index (user_id)
);
create table pets (
id int unsigned not null auto_increment,
name varchar(20) not null,
user_id int unsigned,
primary key (id),
index (user_id)
);


本にはいきなりプチSNSのARクラス図の例がぽんっと掲載されていたので、一気に、Railsアプリ生成から、モデル作成までやってしまおう。

C:\InstantRails\rails_apps>rails sns
C:\InstantRails\rails_apps\sns>ruby script/generate model user
C:\InstantRails\rails_apps\sns>ruby script/generate model community
C:\InstantRails\rails_apps\sns>ruby script/generate model topic
C:\InstantRails\rails_apps\sns>ruby script/generate model pet
      〜略&ログをマージ〜
      create  db/migrate
      create  db/migrate/001_create_users.rb
      exists  db/migrate
      create  db/migrate/002_create_communities.rb
      exists  db/migrate
      create  db/migrate/003_create_topics.rb
      exists  db/migrate
      create  db/migrate/004_create_pets.rb

おっmigrateの履歴が表示された。
migarateを使ってプチSNSを作りたいが、道を踏み外しそうなのでここは我慢


よし、rails.vimを使って、一気にモデルを書いてしまおう。


書いた。こんな感じ。
既に他のモデルで、「has_and_belongs_to_many」や「belongs_to」は記述済みなので、で補完が効いた。

class User < ActiveRecord::Base
  has_and_belongs_to_many :communities
  has_many :topics
  has_one :pet
end

class Community < ActiveRecord::Base
  has_and_belongs_to_many :users
end

class Topic < ActiveRecord::Base
  belongs_to :user
end

class Pet < ActiveRecord::Base
  belongs_to :user
end

これは、本に書いてある通りに入力しただけで、わからないところがある。

  • 関連付けをするキーワードに単数形と複数形が混ざっているところ。


なんでだろ??相手をみて、自分が1の場合は単数形という感じなのかなぁ??
それはあとで出てくるかもしれないので、今はとりあえずこのまま進めてみる。

データを作成する

本ではActiveRecordを使って、Userデータを登録する例があったので、真似をしてActiveRecordからデータ登録をやってみる。


Rails環境のロード&irb機能で行う

  • ユーザーモデルのインスタンスを作る。
    • コンストラクタはハッシュでパラメータ指定。ポイントはやっぱ「カラムは定義してないよ」ってことかなぁ。
  • データベースに登録する
C:\InstantRails\rails_apps\sns>ruby script/console
Loading development environment.
>> tom = User.new(:name => "Tom")
=> #<User:0x48841f0 @attributes={"name"=>"Tom", "password"=>""}, @new_record=true>
>> tom.save
=> true

phpMyAdminで確認したところ、期待通り1件Tomレコードが登録されていた。
簡単なデータ登録だったら、SQLを書くよりこっちの方が楽だったりするかも(笑)


この調子で、Joeさんも登録した。>計2レコード

実は、ちょっと試したい事があったので、やってみたこれをしたらどうなるか。

3件レコードが登録されている?

>> joe.save
=> true
>> joe.save
=> true
>> joe.save
=> true

されていなかった。期待通り1件のみJoeレコードが登録されていた。

ちょっと道をそれて掘り下げてみる

舞波本で、saveメソッドに関して調べてみた。

上記の例に関しては詳しく記載されていなかったが*1、下記の事項がわかった。

ActiveRecordで用意されているメソッド

牽引にはsaveは51ページと書いてあったので、載っていなぁと思っていたら5ページ先の「機能を深く掘り下げのコラム」に、ストライクで掲載されていた。

「このコラムシリーズはRails経験者向けで、初心者は混乱を招く事もあるので、最初のうちは特に読まなくても大丈夫。」と目次に書いてある・・・
やべっ読んじゃった。というかこういう挙動関係は最初から気にしててはいけない事だったのかなぁ>フレームワークの勉強とか

疑問に対する答え(自分なりに書いてあった事をまとめてみた)

SQLでは本来使い分けるのに、なぜインスタンスメソッドではCとUでsaveを共有しているのかというと、
利便性を考えて、createとupdateを使い分けてくれるラッパーとしてsaveメソッドRailsが用意してくれている。
使用者としては、ActiveRecordインスタンス状態を意識せずにDBに登録できる。

※インスタンスメソッドとして、createもあるよ。

へぇ x5。


気になっていた事が、全部解決できた。舞波さんありがとう。>コラムに載せていてくれて
ホント舞波本は役に立つ。


とうことで、やってみた。

>> foo = User.new :name => "foo"
=> #<User:0x4884394 @attributes={"name"=>"foo", "password"=>""}, @new_record=true>
>> foo.create
=> 8
>> foo.create
ActiveRecord::StatementInvalid: Mysql::Error: Duplicate entry '8' for key 1: INSERT INTO users (`name`, `id`, `password`) VALUES('foo', 8, '')
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adaters/abstract_adapter.rb:128:in `log'
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adaters/mysql_adapter.rb:243:in `execute'
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adaters/mysql_adapter.rb:253:in `insert'
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:1811:in `create_without_callbacks'
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/callbacks.rb:24:in `create_without_timestamps'
        from C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/timestamp.rb:3
:in `create'
  • 解説
  1. インスタンスを作る
  2. createする
  3. 結果として登録されたプライマリーキーが返ってくる
  4. もう一度、Create
  5. 制約違反で例外発生


これこれ、この挙動になるんじゃないの??って思ったわけですよ。
配慮してくれてたんだ!!最初から便利なメソッドばかり使ってしまうと、なぜそのメソッドが用意されたのか忘れてしまう事もある。
こうやって有り難味を知っておくと、後々の経験や適切な処理に役立つかもしれない・・・

登録データを取得する

さて、4章の続きに戻って作業を進める。

ActiveRecordで登録したデータを取得する方法は、findメソッドを使うとのこと。

findメソッドは2章でもでてきたきたので、復習の意味も込めてやってみた。

  • :allオプションは配列でオブジェクトが返される
  • :firstオプションはオブジェクトが返される

ワンライナー

>> User.find(:all, :conditions => [ "name = ?" , "Tom" ]).each {|u| puts u.name}
Tom
=> [#<User:0x4860f5c @attributes={"name"=>"Tom", "id"=>"1", "password"=>""}>]
>> User.find(:first, :conditions => [ "name = ?" , "Joe" ])
=> #<User:0x4856ffc @attributes={"name"=>"joe", "id"=>"2", "password"=>""}>

マッピングに関して

  1. マッピングに関して、設定ファイルでの定義はないそうです。
  2. 規約に従う事で明示的にマッピングを指定しなくても大丈夫。
  3. アクセスも規約で。
  4. テーブルにカラム変更がされれば、マッピングにも反映されている。

本にあるとおり、カラムを1個増やしてusersテーブルに電話番号(phone_number)を増やして試してみる。

alter table users add phone_number varchar(20) not null;
>> user = User.find(:first)
=> #<User:0x487ab14 @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>
>> user.phone_number
=> "03-1234-0000"

よし、コンソールからマッピングに反映されている事が確認できた。

既存DBを使って、規約に従えない場合は、数行の設定をする事でカバーできるらしい。

ログ出力

んっ、しまった。本に詳しい説明がないが、こんなことコードが書いてある。
見落としたよ。

登録時のコンソール

>> ActiveRecord::Base.logger = Logger.new(STDOUT)
=> #<Logger:0x484a5a4 @default_formatter=#<Logger::Formatter:0x484a57c @datetime_format=nil>, @progname=nil, @
logdev=#<Logger::LogDevice:0x484a554 @shift_size=nil, @shift_age=nil, @filename=nil, @mutex=#<Logger::LogDevic
e::LogDeviceMutex:0x484a52c @mon_entering_queue=[], @mon_count=0, @mon_owner=nil, @mon_waiting_queue=[]>, @dev
=#<IO:0x2946af4>>, @formatter=nil, @level=0>
>> User.find(:first, :conditions => [ "name = ?" , "Joe" ])
=> #<User:0x4845b58 @attributes={"name"=>"joe", "id"=>"2", "password"=>""}>

一行目のロガーをActiveRecordのクラス変数に格納。
ロガーも活用しながら、デバッグとか。

たぶん、上記のロガーとは関係ないけど、今まで行ったCRUD操作のSQL文がlog/development.logに吐き出されていた。
生成されるSQLが判るのはありがたい。

  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.000000)   SHOW FIELDS FROM users
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.000000)   SHOW FIELDS FROM users
  SQL (0.000000)   BEGIN
  SQL (0.031000)   INSERT INTO users (`name`, `password`) VALUES('Tom', '')
  SQL (0.015000)   COMMIT
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.015000)   SHOW FIELDS FROM users
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('joe', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 2
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 2
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 2
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 2
  SQL (0.000000)   COMMIT
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.000000)   SHOW FIELDS FROM users
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('joe', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 3
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  User Update (0.000000)   UPDATE users SET `password` = '', `name` = 'joe' WHERE `id` = 3
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('jojo', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('jojo', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('hoge', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   BEGIN
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('hoge', '')
  SQL (0.000000)   COMMIT
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.000000)   SHOW FIELDS FROM users
  SQL (0.000000)   INSERT INTO users (`name`, `password`) VALUES('foo', '')
  SQL (0.000000)   Mysql::Error: Duplicate entry '8' for key 1: INSERT INTO users (`name`, `id`, `password`) VALUES('foo', 8, '')
  SQL (0.000000)   Mysql::Error: Duplicate entry '8' for key 1: INSERT INTO users (`name`, `id`, `password`) VALUES('foo', 8, '')
DEPRECATION WARNING: find_all is deprecated and will be removed from Rails 2.0 (use find(:all, ...))  See http://www.rubyonrails.org/deprecation for details. (called from irb_binding at (irb):1)
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Load (0.016000)   SELECT * FROM users 
  User Columns (0.000000)   SHOW FIELDS FROM users
  User Load (0.016000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Columns (0.016000)   SHOW FIELDS FROM users
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Columns (0.000000)   SHOW FIELDS FROM users
  User Load (0.016000)   SELECT * FROM users WHERE (users.`id` IN ('--- :all\n','--- \n:conditions: \n- name = ?\n- Tom\n')) 
  User Load (0.000000)   SELECT * FROM users WHERE (users.`id` IN ('--- :all\n','--- \n:conditions: \n- name = ?\n- Tom\n')) 
  User Load (0.016000)   SELECT * FROM users WHERE (users.`id` IN ('--- :all\n','--- \n:conditions: \n- name = ?\n- Tom\n')) 
  User Load (0.000000)   SELECT * FROM users WHERE (users.`id` IN ('--- :all\n','--- \n:conditions: \n- name = ?\n- Tom\n')) 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') 
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Tom') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Joe') LIMIT 1
  SQL (0.000000)   SET SQL_AUTO_IS_NULL=0
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Joe') LIMIT 1
  User Columns (0.015000)   SHOW FIELDS FROM users
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Joe') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Joe') LIMIT 1
  User Load (0.000000)   SELECT * FROM users WHERE (name = 'Joe') LIMIT 1

バリデーションを使う

これも2章でやったけど、オリジナルValidationの作り方が説明されていた。

class User < ActiveRecord::Base
  has_and_belongs_to_many :communities
  has_many :topics
  has_one :pet

  validates_uniqueness_of :name, :on => :create
  validates_length_of :name, :within => 3..40
  validates_length_of :password, :within => 5..40

  def validate
    # 数値以外にマッチしたらエラー
    errors.add("phone_number", "has invalid format") if phone_number =~ /\D/
  end
end

実行してみた

>> user = User.new
=> #<User:0x4843d1c @attributes={"name"=>"", "phone_number"=>"", "password"=>""}, @new_record=true>
>> user.save
=> false
>> user.name ="hogehoge"
=> "hogehoge"
>> user.password = "hogehoge"
=> "hogehoge"
>> user.phone_number = 12121312
=> 12121312
>> user.save
=> true
>> user.save
=> true
>> user.name = nil
=> nil
>> user.save
=> false
>> u = User.new
=> #<User:0x4884920 @attributes={"name"=>"", "phone_number"=>"", "password"=>""}, @new_record=true>
>> u.save
=> false
>> u.name = "hogehoge"
=> "hogehoge"
>> u.save
=> false
>> u.password = "hogehoge"
=> "hogehoge"
>> u.save
=> false
>> u.phone_number = 1234
=> 1234
>> u.save
=> false
>> u.name = "fugafuga"
=> "fugafuga"
>> u.save
=> true
>> u.name = nil
=> nil
>> u.save
=> false
  • 一意制約違反とか、文字列の長さとかvalidationできた。
  • 自分で定義した、電話番号validationも有効になっているみたい。
  • オリジナル定義したValidationはerrorsにaddすればいいみたい。

テーブルのリレーション

やっとここまできた。
いま自分が一番、覚えたいところ。

規約があるらしい。

  • 1対1
    • has_oneとbelongs_toを指定する。
    • 外部キーを持っている方にはhas_one
    • 外部キーを持っていないほうにはbelongs_to
    • リレーションした、テーブル名のメソッドが自動で定義される。 例) User#petメソッド と Pet.userメソッド
  • 1対多
    • has_manyとbelongs_toを指定する。
    • 外部キーを持っている方にはhas_many
    • 外部キーを持っていないほうにはbelongs_to
    • リレーションした、テーブル名のメソッドが自動で定義される。 例) User#topicメソッド と Topic.userメソッド
  • 多対多
    • 両方のモデルにhas_and_belongs_to_manyを指定する
    • Viewで表示する時に大活躍できる


予め作っておいたモデル

class User < ActiveRecord::Base
  # 〜略〜
  has_one :pet
end
class Pet < ActiveRecord::Base
  belongs_to :user
end


やってみた。

>> tom = User.find :first
=> #<User:0x4879f98 @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>
>> tama = Pet.new :name => "Tama" , :user => tom
=> #<Pet:0x4868004 @attributes={"name"=>"Tama", "user_id"=>1}, @user=#<User:0x4879f98 @attributes={"name"=>"To
m", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>, @new_record=true>
>> tama.save
=> true
  1. ユーザーテーブルからTomのインスタンスを生成する
  2. ペットクラスからTamaインスタンスを生成する時に、リレーション時に自動生成されたuserメソッドを使い、結びつけるインスタンスtomを引数として渡しておく。
  3. tamaインスタンスを保存する

キターっ!

id  name  user_id  
1   pochi NULL 
2   Tama  1 

Tamaレコードにtomのレコードが紐付けられて保存されている。

よし、joeとポチを紐付けて保存しみる。

>> joe = User.find :first , :conditions => [ "name = ?" , "Joe" ]
=> #<User:0x48729a0 @attributes={"name"=>"Joe", "phone_number"=>"", "id"=>"2", "password"=>""}>
>> pochi = Pet.find :first , :conditions => [ "name = ?" , "pochi"]
=> #<Pet:0x48668bc @attributes={"name"=>"pochi", "id"=>"1", "user_id"=>nil}>
>> pochi
=> #<Pet:0x48668bc @attributes={"name"=>"pochi", "id"=>"1", "user_id"=>nil}>
>> pochi.user = joe
=> #<User:0x48729a0 @attributes={"name"=>"Joe", "phone_number"=>"", "id"=>"2", "password"=>""}>
>> pochi.save
=> true
>> pochi
=> #<Pet:0x48668bc @attributes={"name"=>"pochi", "id"=>"1", "user_id"=>2}, @user=#<User:0x48729a0 @attributes=
{"name"=>"Joe", "phone_number"=>"", "id"=>"2", "password"=>""}>, @errors=#<ActiveRecord::Errors:0x4861344 @err
ors={}, @base=#<Pet:0x48668bc ...>>>
  1. Joeレコードを検索しインスタンスを生成
  2. pochiレコードを検索しインスタンスを生成
  3. pochiの情報を確認
  4. Petクラスにリレーション設定で自動生成されたメソッドを使用して、pochiのuserにJoeインスタンスをセットする
  5. 最後にpochiインスタンスを保存


できた!!ちょっと感動・・・

id  name  user_id  
1   pochi 2
2   Tama  1 

次は逆に、ユーザーからペット情報を表示してみる

>> joe = User.find :first , :conditions => [ "name = ?" , "Joe" ]
=> #<User:0x486dff4 @attributes={"name"=>"Joe", "phone_number"=>"", "id"=>"2", "password"=>""}>
>> joe.pet
=> #<Pet:0x4864404 @attributes={"name"=>"pochi", "id"=>"1", "user_id"=>"2"}>
>> joe.pet.name
=> "pochi"
  1. まずはジョーを検索しインスタンスを作って
  2. ジョーのペットは?
  3. ポチだ。
  4. ポチの名前は?
  5. pochi

irb最高。

1対多を試す

やってみた。

>> tom = User.find :first , :conditions => [ "name = ?" ,"Tom" ]
=> #<User:0x4866b50 @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>
>> topic1 = Topic.new(:written_on => Date.parse("2007-11-04") , :title => "kameda" , :content => "win" )
=> #<Topic:0x4835438 @attributes={"title"=>"kameda", "content"=>"win", "written_on"=>#<Date: 4908817/2,0,2299161>, "user_id"=>nil}, @new_record=true>
>> topic1
=> #<Topic:0x4835438 @attributes={"title"=>"kameda", "content"=>"win", "written_on"=>#<Date: 4908817/2,0,2299161>, "user_id"=>nil}, @new_record=true>
>> topic1.user = tom
=> #<User:0x4866b50 @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>
>> topic1.save
=> true
>> topic2 = Topic.new(:written_on => Date.parse("2007-11-05") , :title => "tax" , :content => "The tax problem is occurring.")
=> #<Topic:0x4824d40 @attributes={"title"=>"tax", "content"=>"The tax problem is occurring.", "written_on"=>#<Date: 4908819/2,0,2299161>, "user_id"=>nil}, @new_record=true>
>> topic2.user = tom
=> #<User:0x4866b50 @attributes={"name"=>"Tom", "phone_number"=>"03-1234-0000", "id"=>"1", "password"=>""}>
>> topic2.save
=> true
>> tom.topics
=> [#<Topic:0x4811ee8 @attributes={"title"=>"tax", "id"=>"5", "content"=>"The tax problem is occurring.", "written_on"=>"2007-11-05", "user_id"=>"1"}>, #<Topic:0x4811ec0 @attributes={"title"=>"kameda", "id"=>"4", "content"=>"win", "written_on"=>"2007-11-04", "user_id"=>"1"}>]
  1. まずはトムを検索しインスタンスを作っておいて
  2. 日記1のインスタンスを作る
  3. 日記1のインスタンスのuserにtomインスタンスをセットして
  4. 保存する
  5. 同じく日記にのインスタンスを作って、保存する
  6. トムの日記(トピックス)を確認する
  7. 配列で日記が表示された

できた。


やっぱり、複数形の紐付けや関連に関しては記述も複数形にするってことですね。


なるほど。


最後に多対多をやりたかったが、今日はここで時間切れ。


なんとなく、ActiveRecordの基本的な使い方がわかった気がする。
うまく使えば、見通しの良いアプリケーションと、楽ができそう。

今日のまとめと振り返り

第4章半分まとめ

なんとなく、Railsが使えた気になってしまった。
すごいのは自分ではなくRailsなんだ!勘違いしちゃだめだ。

  • ActiveRecord複数のテーブルが簡単に扱える
  • リレーションの規約は思ったほど難しくない(シンプル)
  • 検索条件も基本形は簡単
  • リレーション条件は双方のモデルに書く
  • リレーション情報は双方のモデルにお互いのクラス名のメソッドが自動生成される
  • validationのオリジナル定義は、validationという名前のメソッドを作る
  • インスタンスのsaveメソッドはラッパー

第4章半分の振り返り(KPT法使うよ)

前回のKPTで「名言」や「らしさ」や「繋がり」を意識したので、Railsの事についてちょっと知りたくなった。
アジャイルRails第二版が手元に欲しい。第一版は図書館で借りたけど、理解する前に返却期間がすぐに来てしまった・・・

ぷちKPT

  • KEEP【良かった事、続けていきたい事】
    • はてなDiaryに引き続き書いて、「見える化」とKPTができた。
    • 掘り下げた事で、メソッドの役割や背景が少し見えた。
    • ある程度、ActiveRecordの使い方がわかった。
    • script/consleを使いまくった。
    • 日付のキャストはto_dではなく、Date.Parseを使うと言う事を覚えた。
  • PROBLEM【悪かったこと、改善したい事】
    • 第四章を終える事ができなかった。
    • LogとActiveRecordの件に関して調べることができなかった。
    • 時間をたっぷり使ったわりには、進む速度が遅かった。
    • 掘り下げはほどほどに
  • TRY【今後取り込みたいこと】
    • LogとActiveRecordの件に関して調べることができなかったので調べる。
    • 掘り下げの検証と時間のバランスに気をつける。
    • 先週後半にちょっと体調を崩したので、勉強にはまって夜遅くまで取り組まない。
    • ActiveRecordの機能を色々使ってみる。(抽象的な事なので優先度低で取り組む)

*1:あとで載っている事がわかった

*2:インスタンスメソッドにも用意されていた。もちろん挙動は違う。クラスメソッドはどんどんinsert。インスタンスメソッドは1件レコードに対してのみ。