質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.87%

Ruby On Rails 複数テーブルから期待するデータを表示させたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 149

koh-s

score 2

前提・実現したいこと

イメージ説明
イメージ説明
イメージ説明
【実現したいこと】
・usersテーブルとattendancesテーブルより各取り出したいデータを取得。
・出勤中のユーザー(ここではSample User)のみ表示。
【試したこと】

Attendance.where.not(started_at: nil)
@users = User.all.includes(:attendances) 


上記で各情報をざっくりですが取り出せていることをdebuggerで確認。

(byebug) Attendance.where.not(started_at: nil)
  Attendance Load (0.2ms)  SELECT  "attendances".* FROM "attendances" WHERE ("attendances"."started_at" IS NOT NULL) LIMIT ?  [["LIMIT", 11]]
#<ActiveRecord::Relation [#<Attendance id: 27, worked_on: "2019-11-27", started_at: "2019-11-27 04:13:00", finished_at: nil, note: nil, user_id: 1, created_at: "2019-11-25 12:45:42", updated_at: "2019-11-27 04:13:49">]>
(byebug) 
(byebug) User.all.includes(:attendances)
  User Load (0.3ms)  SELECT  "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
  Attendance Load (0.3ms)  SELECT "attendances".* FROM "attendances" WHERE "attendances"."user_id" IN (1, 2, 3, 4, 5)
#<ActiveRecord::Relation [#<User id: 1, name: "Sample User", email: "sample@email.com", created_at: "2019-11-25 12:23:13", updated_at: "2019-11-25 12:45:53", password_digest: "$2a$12$R6sK1xJNv2H3HNePLGSYyupr0QJ.3y8O/Av8rexJjOL...", remember_digest: nil, admin: true, affiliation: "テスト部", basic_time: "2019-11-24 23:00:00", work_time: "2019-11-24 22:30:00", superior: false, employee_number: 1, uid: "1", designated_work_start_time: "2019-11-25 00:00:00", designated_work_end_time: "2019-11-25 09:00:00", basic_work_time: "2019-11-24 23:00:00">, 
#<User id: 2, name: "Superior User", email: "superior@email.com", created_at: "2019-11-25 12:23:14", updated_at: "2019-11-25 12:23:14", password_digest: "$2a$12$9QBc.FAHnrY1zttzYHYUJuy1GKfqAJ0gtSmNEDPQiaf...", remember_digest: nil, admin: false, affiliation: "上長部", basic_time: "2019-11-24 23:00:00", work_time: "2019-11-24 22:30:00", superior: true, employee_number: 2, uid: "2", designated_work_start_time: "2019-11-25 13:00:00", designated_work_end_time: "2019-11-24 20:00:00", basic_work_time: "2019-11-24 23:00:00">, 
#<User id: 3, name: "山田 彩花", email: "sample-1@email.com", created_at: "2019-11-25 12:23:15", updated_at: "2019-11-25 12:23:15", password_digest: "$2a$12$W6q1Szgja0UjN4y/ldC7b.Y/1iqtoTnU23ujB3QuDxI...", remember_digest: nil, admin: false, affiliation: "フリーランス部", basic_time: "2019-11-24 23:00:00", work_time: "2019-11-24 22:30:00", superior: false, employee_number: 1, uid: "sample1", designated_work_start_time: "2019-11-25 00:00:00", designated_work_end_time: "2019-11-25 09:00:00", basic_work_time: "2019-11-24 23:00:00">, 
#<User id: 4, name: "松本 美桜", email: "sample-2@email.com", created_at: "2019-11-25 12:23:15", updated_at: "2019-11-25 12:23:15", password_digest: "$2a$12$ujsUYOEtEKtagDmc1HWzuewNj/mXAOhmyVJCO7XXfPj...", remember_digest: nil, admin: false, affiliation: "フリーランス部", basic_time: "2019-11-24 23:00:00", work_time: "2019-11-24 22:30:00", superior: false, employee_number: 2, uid: "sample2", designated_work_start_time: "2019-11-25 00:00:00", designated_work_end_time: "2019-11-25 09:00:00", basic_work_time: "2019-11-24 23:00:00">, 
#<User id: 5, name: "木下 明日香", email: "sample-3@email.com", created_at: "2019-11-25 12:23:16", updated_at: "2019-11-25 12:23:16", password_digest: "$2a$12$CZD7Svm8oq3NgJM8x1nVGeUxmLO7hoqcNBpRpsgKCEi...", remember_digest: nil, admin: false, affiliation: "フリーラン ス部", basic_time: "2019-11-24 23:00:00", work_time: "2019-11-24 22:30:00", superior: false, employee_number: 3, uid: "sample3", designated_work_start_time: "2019-11-25 00:00:00", designated_work_end_time: "2019-11-25 09:00:00", basic_work_time: "2019-11-24 23:00:00">]>
(byebug) 

該当のソースコード

【users_controller.rb】

  def index_attendance
    Attendance.where.not(started_at: nil).each do |attendance|
      if (Date.current == attendance.worked_on) && attendance.finished_at.nil?
        @users = User.all.includes(:attendances)
      end
    end
  end


【index_attendance.erb】

<% provide(:title, "出勤中社員一覧") %>
<h1>出勤中社員一覧</h1><div>
  <table class="table table-bordered table-condensed table-hover text-center table-striped" id="index_attendance">
    <thead>
      <tr>
        <th>社員番号</th>
        <th>名前</th>
      </tr>  
    </thead>
    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.employee_number %></td>
          <td><%= user.name %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>


【attendance.rb】

class Attendance < ApplicationRecord
  belongs_to :user
  require 'csv'

  validates :worked_on, presence: true
  validates :note, length: { maximum: 50 }

  # 出勤時間が存在しない場合、退勤時間は無効
  validate :finished_at_is_invalid_without_a_started_at
  validate :started_at_than_finished_at_fast_if_invalid

  def finished_at_is_invalid_without_a_started_at
    errors.add(:started_at, "が必要です") if started_at.blank? && finished_at.present?
  end

  def started_at_than_finished_at_fast_if_invalid
    if started_at.present? && finished_at.present?
      errors.add(:started_at, "より早い退勤時間は無効です") if started_at > finished_at
    end
  end
end


【user.rb】

class User < ApplicationRecord
  has_many :attendances, dependent: :destroy
  attr_accessor :remember_token
  before_save { self.email = email.downcase }
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 100 }, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
  validates :affiliation, length: { in: 2..30 }, allow_blank: true
  validates :basic_work_time, presence: true
  validates :work_time, presence: true

  def User.digest(string)
    cost = 
      if ActiveModel::SecurePassword.min_cost
        BCrypt::Engine::MIN_COST
      else
        BCrypt::Engine.cost
      end
      BCrypt::Password.create(string, cost: cost)
  end

  def User.new_token
    SecureRandom.urlsafe_base64
  end

  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end

  def authenticated?(remember_token)
    return false if remember_digest.nil? 
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end

  def forget
    update_attribute(:remember_digest, nil)
  end

  def self.import(file)
    CSV.foreach(file.path, headers: true, encoding: 'Shift_JIS:UTF-8') do |row|
      # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成
      user = find_by(id: row["id"]) || new
      # CSVからデータを取得し、設定する
      user.attributes = row.to_hash.slice(*updatable_attributes)
      # 保存する
      user.save
    end
  end

  def self.updatable_attributes
    ["name", "email", "affiliation", "employee_number", "uid", "basic_work_time",
     "designated_work_start_time", "designated_work_end_time", "superior", "admin", "password"]
  end
end


【schema.rb】

ActiveRecord::Schema.define(version: 20191118081810) do

  create_table "attendances", force: :cascade do |t|
    t.date "worked_on"
    t.datetime "started_at"
    t.datetime "finished_at"
    t.string "note"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_attendances_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "password_digest"
    t.string "remember_digest"
    t.boolean "admin", default: false
    t.string "affiliation"
    t.datetime "basic_time", default: "2019-11-24 23:00:00"
    t.datetime "work_time", default: "2019-11-24 22:30:00"
    t.boolean "superior", default: false
    t.integer "employee_number"
    t.string "uid"
    t.datetime "designated_work_start_time", default: "2019-11-25 00:00:00"
    t.datetime "designated_work_end_time", default: "2019-11-25 09:00:00"
    t.datetime "basic_work_time", default: "2019-11-24 23:00:00"
    t.index ["email"], name: "index_users_on_email", unique: true
  end

end


【attendances_controller.rb】

  UPDATE_ERROR_MSG = "勤怠登録に失敗しました。やり直してください。"

  def update
    @attendance = Attendance.find(params[:id])
    if @attendance.started_at.nil?
      if @attendance.update_attributes(started_at: Time.current.change(sec: 0))
        flash[:info] = "おはようございます!"
      else
        flash[:danger] = UPDATE_ERROR_MSG
      end
    elsif @attendance.finished_at.nil? 
      if @attendance.update_attributes(finished_at: Time.current.change(sec: 0))
        flash[:info] = "お疲れ様でした!"
      else
        flash[:danger] = UPDATE_ERROR_MSG
      end
    end
    redirect_to @user
  end

試したこと

if文の条件分岐を潜っても全ユーザーが取得できてしまうことは何となくわかりますが、これをそれぞれに紐づける術がわからずギブアップになります。
モデルにはattendance.rbはbelongs_toを設定。
user.rbはhas_manyを設定しています。

お忙しい中恐れ入りますが、何卒ご教授のほど宜しくお願い致します。

補足情報(FW/ツールのバージョンなど)

ruby 2.6.3
rails 5.1.6

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • winterboum

    2019/11/27 23:44

    usersテーブルとattendancesテーブルより各取り出したいデータを取得

    とありますが、どういうのが取り出したいデータなのかが書かれていないので回答しようが無いです。
    取り出したいデータの条件を書いて下さい。
    出勤中のユーザー(ここではSample User)のみ表示。 とありますが、出勤しているかどうかは何で判断するのでしょうか

    キャンセル

  • koh-s

    2019/11/28 11:37

    ご回答ありがとうございます。
    今回、取り出したいデータとして
    ・usersテーブルからは出社時間(started_atがDBに保存されている)かつ退社時間(finished_at)がnilのユーザー情報。
    ・attendancesテーブルからは本日(Date.current)の日付。
    この2点を紐付けてユーザーの名前(name)と従業員番(employee_number)を取り出すことを目的としています。

    また、出勤判断は出社ボタンを押下されて、attendances_controller.rb内のupdateメソッドでif @attendance.started_at.nil?で条件分岐して判断しています。

    表現が難しく適切にお伝えできているか自身がありません、何かご指摘などがございました際は是非勉強させてください。宜しくお願い致します。

    キャンセル

  • winterboum

    2019/11/28 12:29

    User, Attendance のスキーマ(migration情報)を載せて下さい。

    キャンセル

  • koh-s

    2019/11/28 18:09

    お世話になります。
    「該当のソースコード」最後から2番目に【schema.rb】としてご用意しました。
    お手数おかけして申し訳ございませんがご確認の程、宜しくお願い致します。

    キャンセル

回答 1

checkベストアンサー

0

ああ、やっぱり started_at は UserではなくAttendance の方ですね。
すると絞り込み条件が全て Attendanceの方か、、、、
条件に合うUserを探すのではなく、条件にあう Attendance を探してそのUserのデータを表示する方が、検索式が書きやすいかも。
つまり @attendances = Attendance.includes(:user).where()として、@attendances.each{|attencance| attendance.user.某 }とする。
その方針だと

date = Date.today
Attendance.includes(:user).
  where.not(started_at: nil).
  where(finished_at: nil).
  where(started_at: date.beginning_of_day .. date.end_of_day)


でしょうか。
Userで検索するとどうなるのかな、自信ないですが

User.joins(:attendances).
  where.not(attendances: {started_at: nil}).
  where(attendances: {finished_at: nil}).
  where(attendances: {started_at: date.beginning_of_day .. date.end_of_day})


かなぁ

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.87%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる