What we will do
In this tutorial, we will be adding a Trip model to a Rails app. Each Trip can have one or more Destinations and is created by a User. A Trip also has a start date and end date.
To fullfill this requirements in addition to creating a new Trip model we will also need to do a few changes on existing models. And we will learn a new type of relationship as well, the has_and_belongs_to_many
.
Step 1: Generate the Trip model and migration
Run the following command to generate the Trip model and migration:
1
rails generate model Trip start_date:date end_date:date user:references
This will create a Trip model and a migration file to add the necessary database columns for the Trip model.
Step 2: Set up the Destination-Trip association
In the Trip
model, set up a has_and_belongs_to_many
association with the Destination
model. This will allow a Trip
to have many Destinations, and a Destination
to be part of many Trips.
1
2
3
4
5
6
# app/models/trip.rb
class Trip < ApplicationRecord
has_and_belongs_to_many :destinations
belongs_to :user
end
In the Destination
model, set up a has_and_belongs_to_many
association with the Trip
model.
1
2
3
4
5
6
# app/models/destination.rb
class Destination < ApplicationRecord
has_and_belongs_to_many :trips
has_many :reviews
end
Step 3: Create the join table
Run the following command to create the necessary database table for the has_and_belongs_to_many
association between Trips
and Destinations
:
1
rails generate migration CreateJoinTableTripDestination trip destination
This will create a migration file with the necessary code to create the join table for the has_and_belongs_to_many
association between Trips
and Destinations
. If you are faimiliar with Relational Database, this is an Associative Table that solves an n-n
relationship. But the magic of RoR’s Active Record will handle this for us.
Step 4: Run the migration
Run the migration to create the necessary database columns and tables for the Trip model.
1
rails db:migrate
Step 5: Add validations to the Trip model
Add validations to the Trip model to ensure that a Trip has a start date, end date, and user. Additionally, add a validation to ensure that the end date is the same as or after the start date.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# app/models/trip.rb
class Trip < ApplicationRecord
has_and_belongs_to_many :destinations
belongs_to :user
validates :start_date, presence: true
validates :end_date, presence: true
validate :end_date_after_start_date
validates :user, presence: true
private
def end_date_after_start_date
if end_date && start_date && end_date < start_date
errors.add(:end_date, "must be the same as or after the start date")
end
end
end
Step 6: Add a form for creating a new Trip
Add a form to a view file for creating a new Trip
. The form should include fields for the start date
, end date
, and Destinations
. As a Trip
may have one or manh Destinations, you can use the collection_check_boxes
helper to create checkboxes for selecting one or more Destinations
.
Your view file at app/views/trips/new.html.erb
should look similar to this:
1
2
3
4
5
6
7
8
9
10
11
12
<%= form_for @trip do |f| %>
<%= f.label :start_date %>
<%= f.date_field :start_date %>
<%= f.label :end_date %>
<%= f.date_field :end_date %>
<%= f.label :destinations %>
<%= f.collection_check_boxes :destination_ids, Destination.all, :id, :name %>
<%= f.submit %>
<% end %>
This code will create a form with fields for the Trip. The collection_check_boxes
is a Rails helpe that will create a checkbox for each Destination in the database, allowing the user to select one or more Destinations for the Trip. When we have a lot of Destinations in our database, we may want to change this. But for now this will work fine on our app.
Step 7: Create a new Trip in the controller
In the controller action that handles the form submission, create a new Trip using the form params. Set the user attribute of the Trip to the current user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# app/controllers/trips_controller.rb
class TripsController < ApplicationController
def create
@trip = Trip.new(trip_params)
@trip.user = current_user
if @trip.save
redirect_to @trip
else
render :new
end
end
private
def trip_params
params.require(:trip).permit(:start_date, :end_date, destination_ids: [])
end
end
Note: Keep in mind that your
TripsController
will need more actions to deal with the needed logic. If you need more context on how to that you can check our previous articles: Get started with Ruby on Rails and Expand Your RoR Travel.
Step 8: Display the Trip details
In the view for displaying a single Trip, show the start date, end date, and Destinations for the Trip.
You can add the following code to the app/views/trips/show.html.erb
file:
1
2
3
4
5
6
7
8
<p>Start date: <%= @trip.start_date %></p>
<p>End date: <%= @trip.end_date %></p>
<p>Destinations:</p>
<ul>
<% @trip.destinations.each do |destination| %>
<li><%= destination.name %></li>
<% end %>
</ul>
This code will display the start date, end date, and a list of Destinations for the Trip. The @trip.destinations
method returns an array of Destination
objects associated with the Trip
, and the each loop iterates over the array, displaying the name of each Destination
.
You can also include links for editing and deleting the Trip
, as well as a list of Reviews
for each Destination
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<p>Start date: <%= @trip.start_date %></p>
<p>End date: <%= @trip.end_date %></p>
<p>Destinations:</p>
<ul>
<% @trip.destinations.each do |destination| %>
<li>
<%= destination.name %>
<%= link_to "Edit Destination", edit_destination_path(destination) %>
<%= link_to "Delete Destination", destination, method: :delete, data: { confirm: "Are you sure?" } %>
<% if destination.reviews.any? %>
<ul>
<% destination.reviews.each do |review| %>
<li>
<%= review.rating %>
<%= review.comment %>
<%= link_to "Edit Review", edit_review_path(review) %>
<%= link_to "Delete Review", review, method: :delete, data: { confirm: "Are you sure?" } %>
</li>
<% end %>
</ul>
<% end %>
</li>
<% end %>
</ul>
<%= link_to "Edit Trip", edit_trip_path(@trip) %>
<%= link_to "Delete Trip", @trip, method: :delete, data: { confirm: "Are you sure?" } %>
This code will display the start date, end date, and a list of Destinations for the Trip. For each Destination, it will display a list of Reviews with ratings and comments. It will also include links for editing and deleting each Destination and Review, as well as a link for deleting the entire Trip.
Conclusion
In this tutorial, we learned how to add a Trip
model to a Rails app. We generated the Trip
model and migration, set up the Destination-Trip
association, created the join table, ran the migration, added validations to the Trip
model, added a form for creating a new Trip
, created a new Trip
in the controller, and displayed the Trip
details in the view along with its attributes.
I hope this tutorial was helpful. If you have any questions or comments, please don’t hesitate to reach out.
Happy coding!
Comments