20210503のMySQLに関する記事は2件です。

Railsのみで日付からデータを表示する方法

記事の概要 Webサービス上で日付を入力して「送信」のようなボタンを押すと、その日付に応じた結果が表示されることはよくあるものと思います。これをRailsで実装する手順について解説していきたいと思います。 バージョン ・ruby 2.6.5 ・rails 6.0.3.4 ・mysql 14.14 体重や体脂肪をデータベースに保存してそれを表示するアプリケーションを例にとって考えてみます。 前提として、事前にデータベースに日付・体重・体脂肪・体調の情報が保存されているものとします。 まず、記録を保存する機能として、Body_Record というモデルを用意しています。 app>models>body_record.rb class BodyRecord < ApplicationRecord belongs_to :user, optional: true with_options presence: true do validates :date #日付必須 validates :body_weight #体重必須 validates :fat #体脂肪率必須 end 続いてビューは以下の通りです。 ビューファイルの中身は以下のようになっています(CSSは割愛します) app>views>body_records>index.html.erb <h3 class="input-nav">体重・体脂肪記録を確認する</h1> <% unless @body_record.empty? %> <div class="action-box"> <%= form_tag(search_body_records_path, method: :post) do %> <p>見たい日付を選択してください<input type="date" name="body_record[date]" id="body_record_date"> <input type="submit" value="確認する", class="btn btn-success"></p> <% end %> </div> #テーブル形式で表示するための記述ここから <div class="item-box"> <% @body_record.each do |latest_bodyrecord| %> <div class="latest-box"> <table class="latest-body-data"> <tbody> <tr> <th class="latest-data">日付</th> <td class="latest-value"><%= latest_bodyrecord.date %></td> </tr> <tr> <th class="latest-data">体重</th> <td class="latest-value"><%= latest_bodyrecord.body_weight %>kg</td> </tr> <tr> <th class="latest-data">体脂肪率</th> <td class="latest-value"><%= latest_bodyrecord.fat %>%</td> </tr> <tr> <th class="latest-data">体調</th> <td class="latest-value"><%= latest_bodyrecord.todays_condition %></td> </tr> </tbody> </table> </div> #編集・削除ボタンここから <div class="edit-delete-box"> <li class="edit-box"> <%= link_to "編集する", edit_body_record_path(latest_bodyrecord.id), method: :get, class: "btn btn-info" %> </li> <li class="delete-box"> <%= link_to "削除する", "/body_records/#{latest_bodyrecord.id}", method: :delete, class: "btn btn-danger" %> </li> </div> #編集・削除ボタンここまで <% end %> </div> #テーブル形式で表示するための記述ここまで #データが入っていない際の条件分岐 <% else %> <div class="empty"> <p>記録されたデータはありません</p> </div> <% end %> コントローラー側の処理は以下の通りです。 app>controllers>body_records_controller.rb #〜省略 def index @body_records = BodyRecord.all @body_record = BodyRecord.order(date: :desc).limit(1) end def search @body_records = BodyRecord.all @body_record = BodyRecord.new(body_params) if @body_record.date.present? @body_record = BodyRecord.where('date = ?', "#{@body_record.date}}") else @body_record = BodyRecord.none end render :index end #省略〜 ルーティングは以下の通りです。 config>route.rb resources :body_records do collection do post :search end 処理の流れ まず、コントローラー側のindexアクション内の@body_record = BodyRecord.order(date: :desc).limit(1)の記述により、index.html.erbにアクセスした際は、最新の日付のデータが表示されます。 その後、日付を選択して確認するボタンを押すと、searchアクションが動き、データベースの中から選択された日付のデータを取得して、render :indexの記述によりindex.html.erbを再表示する仕組みになっています。 さらに、もし選択された日付にデータが保存されていなかった場合、 <% unless @body_record.empty? %> 省略 <% else %> <div class="empty"> <p>記録されたデータはありません</p> </div> <% end %> の記述により、「記録されたデータはありません」という表示が出るように条件分岐をしています。 終わりに 今回ご紹介した内容は、Railsで実装しましたが、Ajaxで非同期通信を用いて実装するほうが望ましいのではないかな?と考えています。これからアプリケーションをアップデートする中で、より良い方法や手法をどんどん試していこうと考えます。 間違っていたり、おかしなところがありましたら修正いたしますので、遠慮なくコメントいただければと思います。 最後までお読みいただき、ありがとうございました。 参考記事 【Rails入門】form_tagの使い方まとめ | 侍エンジニアブログhttps://www.sejuku.net/blog/29083 Railsで検索機能を実装する方法を現役エンジニアが解説【初心者向け】https://techacademy.jp/magazine/22330 【Rails】パラメーター値の取得(paramsメソッドの使い方https://qiita.com/yuki_0920/items/9f450a51c74407645a3b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

解決!Datetime型と文字列の比較でハマった話 & 開発環境と本番環境のdbは揃えておいた方がいいねって話  

1. 環境 Ruby : 2.6.6 Rails : 6.0.3.6 db : sqLite 3 (開発環境) | postgreSQL(本番環境) 2. 背景 コロナ禍で、昼食時に会社の食堂に入る人数をコントロールする簡単な席予約アプリを作っている時にハマった話です。 views で、予約したい日付(date)と、時間帯(slot)を選ばせて、controllersのcreateアクションに送る、その際の条件として下記の4つの条件を設定。 1) 過去の日付を予約させない 2) 土日を予約させない 3) 同じ人が同じ日に予約をさせない(昼食は一日一回であろうと想定) 4) 同じ日の同じスロットに入る人数の調整 (現在はテストとして1人以上予約が入ることを阻止) 1),2)は問題なし。しかし3),4)が開発環境ではうまく動作するのに、Heroku上では動作しない。つまり同じ人が同じ日に予約できてしまい、同じ日の同じスロットに入る人数の調整ができない (テストとして1人以上予約が入ってしまう)という現象が発生。 素晴らしいメンターさんの指導で解決に至ったという話です。 3. 結論 (1) timeカラムの設定が datetime 型なので、格納されているレコードは当然datetime型。 一方viewsから送信された値は、"2020-12-11" などという文字列となる。 datetime型と文字列では比較ができない。 (2) よって viewsから送信された値"2020-12-11"を、Timeクラスのzoneメソッド(time zone をつける)とparseメソッド(文字列から日付への変換)を使用してdatetime型に変換する。 @booking_date = Date.parse(params[:booking][:date])  ↓ @booking_date = Time.zone.parse(params[:booking][:date]) (3) それでは、なぜ development環境下では動作していたものが production環境下では動作しなかったのか? どうやらこれは、development および test 環境下では sqlite3 を使用、production環境下ではPostgresQL を使用していたことに起因するようだ。development 環境下で動作したのは、sqlite3が文字列と datetime の比較をやってくれていたからで、PostgresQLはそれら型が違うものの比較はしてくれないというのが原因だった。 ・・・ということで解決! 学びとしては、development, test, production の各環境で使用するdbは同じものを使用するべきであるということ。あとはデータの型に注意ということ。 app/views/bookings/new.html.erb <div class="wrapper"> <h2>Book your table here.</h2> <%= form_with(model: @booking, local: true) do |f| %> <div class="form-group"> <%= f.label :date %> <%= f.date_field :date, class: 'form-control' %> #これで日付を飛ばすと文字列データに・・・ </div> <div class="form-group"> <%= f.label :slot %><br> <%= f.select :slot, [["11:30~11:45", "11:30~11:45"],["11:45~12:00", "11:45~12:00"], ["12:00~12:15", "12:00~12:15"],["12:15~12:30", "12:15~12:30"], ["12:30~12:45", "12:30~12:45"],["12:45~13:00", "12:45~13:00"], ["13:00~13:15", "13:00~13:15"],["13:15~13:30", "13:15~13:30"]], include_blank: "select slot" %> </div> <div class="form-group"> <%= f.submit "Book my table",class: "button-create" %> </div> <% end %> app/controllers/bookings_controller.rb def create @user = current_user @booking_date = Date.parse(params[:booking][:date])   #どうも↑がおかしい!   #のちに@booking_date = Time.zone.parse(params[:booking][:date])に変更して解決! @booking_slot = params[:booking][:slot] # 予約ができる日は本日以降の未来とする if @booking_date < Date.today flash[:danger]= "Head for the future!" redirect_to new_booking_path # 日曜(0)か土曜(6)は予約をさせない。 Date.strptime(@booking_date).wday == 6 elsif @booking_date.wday == 0 || Date.strptime(@booking_date).wday == 6 flash[:danger]= "Dont' be a slave of your job!" redirect_to new_booking_path else #同じ人が同じ日に予約を入れる事を防ぐ…昼食は一日一回であろうと想定 if Booking.where(user_id: @user.id).where(date: @booking_date).any? flash[:danger]= "Double booking in the the day! Please check." redirect_to root_path #同じ日の同じスロットに入る人数の調整…現在はテストとして1人以上予約が入ることを阻止 elsif Booking.where(date:@booking_date).where(slot:@booking_slot).count >= 1 flash[:danger]= "That slot is fully occupied! Please try other slot." redirect_to new_booking_path else app/db/schema.rb ActiveRecord::Schema.define(version: 2020_10_30_034620) do create_table "bookings", force: :cascade do |t| t.datetime "date" #dateカラムにdatetime型を設定 t.string "slot" t.integer "user_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.index ["user_id"], name: "index_bookings_on_user_id" end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む