Search code examples
ruby-on-railsassociationsmodels

Proper model set up


I am new to stackoverflow, so my apologies if this is formatted poorly.

In my current project I have a model Driver, which has many trips. Those trips have many mileages, backhauls, picks, drops and hours. When I create a new trip, i want to be able to associate it to the driver, but I also want to be able to add the mileages, backhauls, picks and drops and hours on the same page. I am unsure how to structure my routes for this. I have been successful in creating a trip for a driver without adding on the additional models to the trip but from there I am stumped. I have only created the mileage model/controller so far for what needs to be associated with the trip. Any nudge in the right direction would be greatly appreciated.

Driver Model

class Driver < ApplicationRecord
  has_many :trips
end

Trip Model

class Trip < ApplicationRecord
  belongs_to :driver
  has_many :mileages
  accepts_nested_attributes_for :mileages
  default_scope {order(date: :asc)}
  validates :total, presence: true
  validates :date, presence: true
  validates_uniqueness_of :trip_number, :allow_nil => true, :allow_blank => 
  true 

end

Mileage Model

class Mileage < ApplicationRecord
  belongs_to :trip
end

Trips controller

def index
  @trips = Trip.all
end

def show
end

def new
  @driver = Driver.find(params[:driver_id])
  @trip = Trip.new
end

def edit
end

def create
  @driver = Driver.find(params[:driver_id])
  @trip = Trip.new(trip_params)
  @driver.trips.create(trip_params)
  respond_to do |format|
    if @driver.trips.create(trip_params)
      flash[:notice] = "Trip successfully created"
      redirect_to new_driver_trip_path(@driver)
    else
      flash[:warning] = "Unable to create trip"
      redirect_to new_driver_trip_path(@driver)
    end
end
private

  def set_trip
    @trip = Trip.find(params[:id])
  end

  def trip_params
    params.require(:trip).permit(:trip_number, :date, :driver_id, :total)
  end

end

Mileage Controller

  def new
    @mileage = Mileage.new
  end

  def create
    @mileage.create(mileage_params)
  end

  private

    def mileage_params
      params.require(:mileage).permit(:miles, :rate, :total)
    end
  end
end

Driver Controller

  def index
    @drivers = Driver.all
  end

  def show
  end

  def new
    @driver = Driver.new
  end

  def edit
  end

  def create
    @driver = Driver.new(driver_params)

    respond_to do |format|
      if @driver.save
          format.html { redirect_to @driver, notice: 'Driver was 
           successfully created.' }
          format.json { render :show, status: :created, location: @driver }
      else
        format.html { render :new }
        format.json { render json: @driver.errors, status: 
         :unprocessable_entity }
      end
    end
  end
  private

    def set_driver
      @driver = Driver.find(params[:id])
    end

    def driver_params
      params.require(:driver).permit(:first_name, :last_name, :email, :unit)
    end
end

Solution

  • If you want to create nested models on the same page. i.e. milages within trip page using accepts_nested_attributes_for, You can use cocoon gem.

    https://github.com/nathanvda/cocoon

    Drifting Ruby has a video that shows the process in detail that is easy to follow. https://www.youtube.com/watch?v=56xjUOAAZY8

    You can do it manually as well but it will require a little bit more work. With cocoon you will do have a Driver Controller and Trip controller but you don't need a Milage controller since it is handled with nested_attributes via Trip Controller.

    If you want to do it manually, you will need a bit of JavaScript. You can follow Ryan Bates RailsCast on this topic. railscasts.com/episodes/196-nested-model-form-revised