LoginSignup
0
0

More than 1 year has passed since last update.

【超簡単】Railsで料理や食材のカロリー検索してみた〜

Last updated at Posted at 2023-02-28

やりたいこと

*1. 食材データをデータベースに保存
(料理名、食材名とその成分が一覧になってるexcel)

参考:日本食品標準成分表2020年版(八訂) 第2章(データ) (Excel:1.9MB)

*2. カロリー検索によって近いカロリーの料理を表示

rails アプリの作成

terminal
rails 6.1.5 new rails_food_app
cd rails_food_app

Excelのダウンロード

このドライブ内にあるfood_list.xlsxファイルをダウンロードし、railsアプリ内の「assets/Excel/food_list.xlsx」に移す
(Excelフォルダは自分で作成)
Screenshot 2023-02-28 18.28.56.png
こんな感じになってればおけです!

ちなみにこのExcelの1列目がそのままカラム名になります。
詳細↓↓↓

意味 カラム名
食品名 food_name
カロリー kcal
水分 water
タンパク質 protein
脂質 lipid
食物繊維総量 fibre
炭水化物 carbohydrate
ナトリウム Na
カルシウム Ca
マグネシウム Mg
Fe
ビタミンC vitaminC
食塩相当量 salt

Excelファイルからデータベースへ書き込み

今回はrooというgemを使ってExcel内の情報を取得しデータベースへ書き込んでいきます

Gemfileの一番下に追記

Gemfile
gem "roo", "~> 2.9.0"
terminal
bundle install

読み込むための下準備
controller、table作成、カラム準備

terminal
rails g controller foods
rails g model Food
db/migrate/~~~~~~~~~~_create_foods.rb
class CreateFoods < ActiveRecord::Migration[6.1]
  def change
    create_table :foods do |t|
# 追記
      t.string :food_name
      t.integer :kcal
      t.float :water
      t.float :protein
      t.float :lipid
      t.float :fibre
      t.float :carbohydrate
      t.float :Na
      t.float :Ca
      t.float :Mg
      t.float :Fe
      t.float :vitaminC
      t.float :salt
# 追記終わり

      t.timestamps
    end
  end
end

development.sqlite3ファイルを削除

terminal
rails db:migrate

schema.rbでfoodsテーブルができてたらok ↓↓
image.png

controller

foods_controller.rb
class FoodsController < ApplicationController
  def index
  end
end

view/foods/の下にindex.html.erbを作成

index.html.erb
<%= button_to '食材登録', {controller: 'foods', action: 'read'} %>

ここではのちに作るreadアクションを呼び出しています
つまりこのボタンが押されるとreadアクションでexcelから情報の読み取り、データベースへの書き込みをしているということです

config/routes.rb
  get "/foods/index", to: "foods#index"
  post "/foods/read", to: "foods#read"

readアクションを定義していきます

foods_controller.rb
  def read
    # Excelファイルの指定
    excel = Roo::Excelx.new('app/assets/Excel/food_list.xlsx')
    # Excelファイル内のシートを指定
    sheet = excel.sheet('Sheet1')
    # シートを1列ごとにハッシュの形にして取得
    # <指定したいkey>: '<Excelの1列目の名前>'
    # っていう感じになってます
    rows = sheet.parse(
      food_name: 'food_name',
      kcal: 'kcal',
      water: 'water',
      protein: 'protein',
      lipid:'lipid',
      fibre:'fibre',
      carbohydrate:'carbohydrate',
      Na:'Na',
      Ca:'Ca',
      Mg:'Mg',
      Fe:'Fe',
      vitaminC:'vitaminC',
      salt:'salt'
    )
    # 取得したハッシュを順にデータベースへ書き込む
    rows.each do |row|
      Food.create(row)
    end
  end

ここまでで書くとこは終了です
一旦localhostを立ち上げてみましょう

terminal
rails s

indexページ
http://localhost:3000/foods/index
image.png
こんなボタンが出てればオッケーです
押すと長めのロードが入ると思います
理由はこのボタンが押されるとfoodsコントローラのreadアクションが呼び出されます
つまり、Excelのすべての行をデータベースに書き込んでいる途中って感じです
1分くらいで僕は終わりました
終わったらデータベースにちゃんと値が保存されているか確認しましょう

terminal
rails c
Food.all
Food.find(1)

感な感じで適当にやったらデータ保存できていることが確認できると思います
image.png
ここまで行けたらassets/Excel/food_list.xlsxファイルは削除しても構いません
index.html.erbのボタンは本番環境でも同様にデータを書き込むためにコメントアウトしておくことをおすすめします

料理の表示

今回は食品一覧の中からランダムで1つとっていきたいと思います

foods_controller.rb
  def index
    @rand_food = Food.where( 'id >= ?', rand(Food.first.id..Food.last.id) ).first
  end

これはFoodの中の最初のidから最後のidまでのランダムな1つのidをrand関数で生成し、取得しています

views/foods/index.html.erb
<%# <%= button_to '食材登録', {controller: 'foods', action: 'read'} %>

<h1>今日はこれだ</h1>

<h2><%= @rand_food.food_name %></h2>

<p><%= link_to 'もう一回', foods_index_path %></p>

Screenshot 2023-02-28 19.25.32.png
こんな感じになりました〜

カロリー検索

index.html.erbの一番下に追記

views/foods/index.html.erb
<h1>カロリー検索</h1>

<%= form_tag({controller:"foods",action:"index"}, method: :get) do %>
  <%= number_field_tag :calorie_search %>kcal <br>
  <%= submit_tag '検索する'  %>
<% end %>

<div class="calories-result">
  <% @near_calories.each do |food| %>
    <h2><%= food.food_name %> <%= food.kcal %></h2>
  <% end %>
</div>
foods_controller.rb
  def index
    @rand_food = Food.where( 'id >= ?', rand(Food.first.id..Food.last.id) ).first

# 追記
    @near_calories = []
    if params[:calorie_search].blank?
      @near_calories = []
    else
      @near_calorie_id_list = Food.pluck(:id, :kcal).min_by(5) { |x, y| (params[:calorie_search].to_i - y.to_i).abs }
      @near_calories = @near_calorie_id_list.map!{|x, y| Food.find(x)}
    end
# 追記終わり
  end

解説

@near_calorie_id_list = Food.pluck(:id, :kcal).min_by(5) { |x, y| (params[:calorie_search].to_i - y.to_i).abs }

Food.pluck(:id, :kcal)
Foodの中のid、kcalのカラムだけ取得して2次元配列にする
min_by(5)
次に書く条件の中で最小の5つを取得する
{ |x, y| (params[:calorie_search].to_i - y.to_i).abs }
min_byの条件で、(検索した値) - (データの各カロリー)の結果順にしている

今日のコードたち

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0