Here's the "cheatsheet" for all of the available actions for a resource, from the last lesson:
Action | Use Case | HTTP Method | Path |
---|---|---|---|
index | Read (display) a list of all tacos | GET | /tacos |
show | Read (display) information on a single taco | GET | /tacos/123 |
new | A form to create a new taco | GET | /tacos/new |
create | Receives the information from the "new" form and creates the new taco | POST | /tacos |
edit | A form to update an existing taco | GET | /tacos/123/edit |
update | Received the information from the "edit" form and updates the existing taco | PATCH | /tacos/123 |
destroy | Processes a request to destroy an existing taco | DELETE | /tacos/123 |
In this lesson, we're going to concentrate on creating new records. As briefly explained in the last lesson, creating new instances of a resource involves two actions – the new
and create
actions. This is simply due to the nature of HTML – we need to display a form to fill out – the new
action – and also need to have an action to accept the form data as parameters, do the work of creating the new record in the database, and send the user on their way – the create
action.
The new
Action and Rails' Form Helpers
Let's continue building out our CRM application and give the user the ability to create a new company. Here's the code for both the controller and view:
def new
render :template => "companies/new"
end
To break it down, we have a new
action defined in our controller, and the only thing happening is that we're rendering the view template file which will have the form. Note that the new doesn't save anything to the database; it's just the request to display the form to the user.
Moving over to the view, we see a fairly vanilla HTML form with the fields we want to save for a company.
<form action="/companies" method="post">
<p>
<label for="name">Name</label>
<input type="text" name="name">
</p>
<p>
<label for="city">City</label>
<input type="text" name="city">
<label for="state">State</label>
<input type="text" name="state">
</p>
<button>Submit</button>
</form>
We can also see that this form is designed to submit to the create
action. How do we know it's the create
action? Recall that the create
action is the combination of the pluralized resource name as the URL (i.e. /companies
) and the HTTP POST
method.
The create
Action
Try filling out and submitting the form!

That's ok – we don't fear errors! Of course, Rails is letting us know that we don't have a create
action in our controller, so let's create one:
def create
end
And let's go back, fill out, and submit the form again... and this time, nothing happens.
This is because we've done something a little different this time than we've done before. Remember that, in the past, when we've gotten this error, we solved it by creating a view file – that is, if we were to add a file called create.html.erb
in our app/views/companies/
folder, this error would go away. But that's not what we want in this case. Not all actions have views! In this case, we want our action to be a method in our controller that only does three things:
- Grab the information the user filled out in the form
- Use the information to create a new
Company
and save it to the database - Send the user back to the full list of companies
Let's work backwards and add a line of code to our action that will do the last thing – send the user back to the list of companies.
def create
redirect_to "/companies"
end
Submit the form again, and we'll notice that we're back at the list of companies. But the new company we tried to create isn't there – of course, this is because we didn't do anything with the data submitted.
Remember how we've previously talked about the transfer of state between pages/actions on the web? And how each request-response cycle doesn't really know about the previous ones, or really anything about the order things have happened in the user's journey? The transition from new
to create
that we're doing here is really no different. We have to get the data that the new
action has sent to our create
action, and that's done in the same way we've done before – through the params
hash.
Let's comb through our server log (that's the output from the rails server
command) and look for the POST
ing of the form. We'll find this:
Started POST "/companies" for ...
Processing by CompaniesController#create as HTML
Parameters: {"name"=>"Netflix", "city"=>"Los Gatos", "state"=>"CA"}
Ah! The data the user has typed into the form is, indeed, in the params
hash. So let's assign the company values using the params
hash in our controller!
def create
@company = Company.new
@company["name"] = params["name"]
@company["city"] = params["city"]
@company["state"] = params["state"]
@company.save
redirect_to "/companies"
end
If we go back, fill out the form, and submit... success!
Try it!
What better way to get some practice with creating records than by creating our own social network!
- You'll find that there's a
PostsController
and anindex
action/view set up already in the project. - Add a
new.html.erb
view and correspondingnew
action in the controller; create a form innew.html.erb
that will allow the end-user to input a post'sauthor
,body
, andimage
(seedb/schema.rb
).author
is the author's name (e.g. Brian),body
is the text body of the post (e.g. Don't these tacos look awesome?) andimage
is simply the address of an image on the Internet. - For example, we can head over to https://unsplash.com/ and search for square images of tacos – right-click and grab the URL of the image.
- Add a
create
action to accept values entered into the form and redirect back to the page with all posts.
Handling Relationships
Creating a company is a good example of a single-model form. But what about contacts? Contacts belong to a company, and the contacts
table has a contact_id
to fill in – how do we handle a situation like this?
There are a couple of good ways to treat a model form that requires a relationship. The first way would be, as we've done already, to use query string parameters to transfer state from one action to the next. For example, we could create a link on, say, the companies/show
page that allows our user to "Create a new contact for this company". Let's give that a try.
<p>
<a href="/contacts/new?company_id=<%= @company.id %>">New contact for <%= @company.name %></a>
</p>
This is fairly straightforward to implement and the user experience is not bad, either. Clicking on the link will take us to the contacts/new
action, transferring the company_id
of the company for which we want to create a contact. For now, the code in the new
action simply renders the view with the form:
def new
render :template => "contacts/new"
end
And then we can add a form in the view with inputs for the contact data like this:
<h1>New Contact</h1>
<form action="/contacts" method="post">
<p>
<label for="first_name">First Name</label>
<input type="text" name="first_name" id="first_name">
<label for="last_name">Last Name</label>
<input type="text" name="last_name" id="last_name">
</p>
<p>
<label for="email">Email</label>
<input type="text" name="email" id="email">
<label for="phone_number">Phone</label>
<input type="text" name="phone_number" id="phone_number">
</p>
<p>
<label for="company_id">Company ID</label>
<input type="text" name="company_id" id="company_id">
</p>
<button>Submit</button>
</form>
And the create
action in the controller with code assigning each value and redirecting back to the company.
def create
@contact = Contact.new
@contact["first_name"] = params["first_name"]
@contact["last_name"] = params["last_name"]
@contact["email"] = params["email"]
@contact["phone_number"] = params["phone_number"]
@contact["company_id"] = params["company_id"]
@contact.save
redirect_to "/companies/#{@contact["company_id"]}"
end
This will all work, but the user experience is lacking. The user won't know what the company's id
value is; we can't expect them to recognize they just came from /companies/1
and then enter a 1 as the company id. Instead, we should handle that for them.
Good news, we can! In the link to this new form, we included the company's id in the query string /contacts/new?company_id=1
. And since it's in the query string, we can access it via the params
hash: params["company_id"]
. To pre-populate the company_id input, change the html to this:
<p>
<label for="company_id">Company ID</label>
<input type="text" name="company_id" id="company_id" value="<%= params["company_id"] %>">
</p>
By adding the value
attribute to the input, the form field will already have the company id in it for the user. However, it probably shouldn't be a user-editable field. As a text input, the user can see 123
(or whatever the ID of the company is), and has the ability to edit that field. Instead, there is a different type of HTML form field we can use, one that's designed precisely for this purpose – a hidden field. Here's what the form HTML looks like with the hidden input:
<h1>New Contact</h1>
<form action="/contacts" method="post">
<p>
<label for="first_name">First Name</label>
<input type="text" name="first_name" id="first_name">
<label for="last_name">Last Name</label>
<input type="text" name="last_name" id="last_name">
</p>
<p>
<label for="email">Email</label>
<input type="text" name="email" id="email">
<label for="phone_number">Phone</label>
<input type="text" name="phone_number" id="phone_number">
</p>
<!-- transfer state from the previous action to the next -->
<input type="hidden" name="company_id" value="<%= params["company_id"] %>">
<button>Submit</button>
</form>
Now, the company_id
will be successfully included each step of the way – we've transferred state all the way from the <a>
tag to create a new contact for a company, through the form, and finally, to the create
action.