Reputation: 1181
I'm developing an app in which users can have a personal food diary.
It's pretty simple: a user can search for a nutrient on a Nutrients table (let's say milk) - currently through a pretty simple search form - and then should be able to save the amount he consumed of this nutrient (together with the nutrient_id) on a second table, which is called Diaries (that holds the nutrient_id as foreign key and an integer field called "amount").
My search works. I can also create new records in Diaries but I have to type in the nutrient_id manually.
My question is now how I can make this easily work? Ideally a user finds a nutrient clicks on it and will be redirected to a page that shows this nutrient together with a field for "amount" and a save button to save both information (nutrient_id and amount) on the Diaries table.
At the end of the day I think the user will be directed to the new action of my diary controller - the question is how my app sets the nutrient_id for this action for the nutrient the user selected before?
Sorry if this is a too simple question but I just started coding few weeks ago.
Thanks a lot for help!
My code looks like as follows:
nutrient.rb
class Nutrient < ActiveRecord::Base
has_many :diaries
def self.search(search)
where("name LIKE ?", "%#{search}%")
end
end
diary.rb
class Diary < ActiveRecord::Base
belongs_to :nutrient
end
nutrients_controller.rb
class NutrientsController < ApplicationController
before_action :set_nutrient, only: [:show, :edit, :update, :destroy]
# GET /nutrients
# GET /nutrients.json
def index
@nutrients = Nutrient.all
end
def search
if params[:search]
@nutrients = Nutrient.search(params[:search]).order("created_at DESC")
if @nutrients.present?
@nutrients
else
flash[:notice] = "Nutrient not found in database"
end
end
end
# GET /nutrients/1
# GET /nutrients/1.json
def show
end
# GET /nutrients/new
def new
@nutrient = Nutrient.new
end
# GET /nutrients/1/edit
def edit
end
# POST /nutrients
# POST /nutrients.json
def create
@nutrient = Nutrient.new(nutrient_params)
respond_to do |format|
if @nutrient.save
format.html { redirect_to @nutrient, notice: 'Nutrient was successfully created.' }
format.json { render :show, status: :created, location: @nutrient }
else
format.html { render :new }
format.json { render json: @nutrient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /nutrients/1
# PATCH/PUT /nutrients/1.json
def update
respond_to do |format|
if @nutrient.update(nutrient_params)
format.html { redirect_to @nutrient, notice: 'Nutrient was successfully updated.' }
format.json { render :show, status: :ok, location: @nutrient }
else
format.html { render :edit }
format.json { render json: @nutrient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /nutrients/1
# DELETE /nutrients/1.json
def destroy
@nutrient.destroy
respond_to do |format|
format.html { redirect_to nutrients_url, notice: 'Nutrient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_nutrient
@nutrient = Nutrient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def nutrient_params
params.require(:nutrient).permit(:name)
end
end
diaries_controller.rb
class DiariesController < ApplicationController
before_action :set_diary, only: [:show, :edit, :update, :destroy]
# GET /diaries
# GET /diaries.json
def index
@diaries = Diary.all
end
# GET /diaries/1
# GET /diaries/1.json
def show
end
# GET /diaries/new
def new
@diary = Diary.new
end
# GET /diaries/1/edit
def edit
end
# POST /diaries
# POST /diaries.json
def create
@diary = Diary.new(diary_params)
respond_to do |format|
if @diary.save
format.html { redirect_to @diary, notice: 'Diary was successfully created.' }
format.json { render :show, status: :created, location: @diary }
else
format.html { render :new }
format.json { render json: @diary.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /diaries/1
# PATCH/PUT /diaries/1.json
def update
respond_to do |format|
if @diary.update(diary_params)
format.html { redirect_to @diary, notice: 'Diary was successfully updated.' }
format.json { render :show, status: :ok, location: @diary }
else
format.html { render :edit }
format.json { render json: @diary.errors, status: :unprocessable_entity }
end
end
end
# DELETE /diaries/1
# DELETE /diaries/1.json
def destroy
@diary.destroy
respond_to do |format|
format.html { redirect_to diaries_url, notice: 'Diary was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_diary
@diary = Diary.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def diary_params
params.require(:diary).permit(:nutrient_id, :amount)
end
end
routes.rb
Rails.application.routes.draw do
resources :diaries
resources :nutrients do
collection do
get :search
end
end
_form.html.erb (for a new diary record)
<%= form_for(@diary) do |f| %>
<% if @diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% @diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nutrient_id %><br>
<%= f.number_field :nutrient_id %>
</div>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Upvotes: 0
Views: 212
Reputation: 5633
There are several ways to resolve this, one the quickest and most likely the best approach to me would be to nest the diaries
under nutrients
, since diaries belongs_to :nutrients
resources :nutrients do
resources :diaries
collection do
get :search
end
end
This way your all your diaries
path method would accept a @nutrient argument and your route would be like: /nutrients/4/diaries/1
So in your diaries_controller, you could have:
class DiariesController < ApplicationController
before_action :set_nutrient
def new
@diary = @nutrient.diaries.new
end
def create
@diary = @nutrient.diaries.new(diary_params) # you can safely remove the nutrient_id from the strong params
... remaining logic here
end
...
private
def set_nutrient
@nutrient ||= Nutrient.find(params[:nutrient_id])
end
# You cans skip/ignore this method, if you don't want to be too strict
def set_diary
@diary ||= @nutrient.diaries.find(params[:id])
end
end
Then in your view, you could then have:
<%= form_for([@nutrient, @diary]) do |f| %>
<% if @diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% @diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
As I mentioned, there are still other ways of doing this, as you could also use hidden_fields
however, this seem to be the cleanest way to me.
If you don't always want your diaries routes to be nested, you could expect: [the list of actions that should not be nested, eg show], both on your before_action
and your routes' resources :diaries
. Hope I'm able to help or let me know other confusions you may encounter.
Upvotes: 2