第14回: ユーザーとタスクの関連づけ

2010/05/29

前々回前回 で、Devise を使ったユーザー認証の仕組みをサンプルアプリケーションに組み込みました。

今回は、ユーザーとタスクの関連づけを行います。

ユーザーのシードデータ

データベースをリセットするたびに、手動でユーザー登録作業を行うのは面倒なので、ユーザーのシードデータを用意しましょう。

db/seeds.rb を修正します。

table_names = %w(users tasks)

(省略)

新規ファイル db/seeds/development/users.rb を作成します。

%w(alpha beta gamma).each do |name|
  user = User.new(
    :email => "#{name}@example.com",
    :password => "#{name}...",
    :password_confirmation => "#{name}..."
  )
  user.save
  user.confirm!
end

user.save でユーザーアカウントが登録されます。user.confirm! でアカウントの確認手続きが完了します。

シードデータを投入します。

% rake db:reset

モデル間の関連づけ

まず、tasks テーブルのマイグレーションファイルを修正し、整数型の user_id カラムを追加します。

class CreateTasks < ActiveRecord::Migration
  def self.up
    create_table :tasks do |t|
      t.integer :user_id
      t.string :name
      (省略)

マイグレーションをやり直します。

% rake db:migrate:reset

User モデルと Task モデルの関連づけを行います。

app/models/user.rb を修正します。

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :lockable and :timeoutable
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation

  has_many :tasks
end

最後の行に has_many :tasks を追加しています。

次に、app/models/task.rb を修正します。

class Task < ActiveRecord::Base
  belongs_to :user

  scope :done, where(:done => true).order("due_date")
  scope :undone, where(:done => false).order("due_date")
end

これで、User と Task が「1対多」の関係で結びつけられました。

tasks コントローラの修正

現在の index アクションは次のようになっています。

  def index
    @task = Task.new
    @tasks = Task.undone.paginate(:page => params[:page], :per_page => 10)
  end

これを次のように修正します。

  def index
    @task = Task.new
    @tasks = current_user.tasks.undone.paginate(:page => params[:page], :per_page => 10)
  end

この修正により、ログイン中のユーザー(current_user)の未完了タスクが 10 個ずつ表示されるようになります。

同様に done アクションを修正します。

  def done
    @task = Task.new
    @tasks = current_user.tasks.done.paginate(:page => params[:page], :per_page => 10)
    render :action => 'index'
  end

続いて create アクションを修正します。現在のソースコードは次の通り。

  def create
    @task = Task.new(params[:task])
    if @task.save
      redirect_to :tasks
    else
      @tasks = Task.all(:conditions => { :done => false }, :order => "due_date")
      render :action => 'index'
    end
  end

else の次の行は、ページネーションを導入したときに index アクションと同様に修正しておくべきでしたが、うっかりそのままになっていました。

修正後は、このようになります。

  def create
    @task = Task.new(params[:task])
    @task.user = current_user
    if @task.save
      redirect_to :tasks
    else
      @tasks = current_user.tasks.undone.paginate(:page => params[:page], :per_page => 10)
      render :action => 'index'
    end
  end

@task.user = current_user の行が、ユーザーとタスクの関連づけを行っている部分です。

タスクのシードデータ

タスクのシードデータ db/seeds/development/tasks.rb を修正します。

alpha = User.find_by_email('alpha@example.com')
beta = User.find_by_email('beta@example.com')

Task.create(:name => "Task 0", :due_date => Date.today, :done => true, :user => alpha)

1.upto(100) do |n|
  Task.create(:name => "Task #{n}", :due_date => n.days.from_now, :done => false,
    :user => n.even? ? alpha : beta)
end

偶数番号のタスクは alpha ユーザーと、奇数番号のタスクは beta ユーザーと結びつけています。

シードデータを投入します。

% rake db:reset

ブラウザで動作確認

alpha ユーザーでログインします。

画面キャプチャ1

すると、偶数番号のタスクだけが表示されています。

画面キャプチャ2

今日の日付で「電球を買う」というタスクを追加します。

画面キャプチャ3

「Sign out」リンクをクリックしてログアウトし、beta ユーザーでログインすると、奇数番号のタスクだけが表示されます。

画面キャプチャ4