The Best Way to Learn Forms in Rails — Create Some Manually

There are many ways to create forms in rails. You could install one of the many form creation gems to help you through the process, or just simply use a built in Rails helper method. If you think about it, Rails is all about forms… it’s what makes a rails app a rails app and lets users interact with our webpage.
Knowing the ins and outs of forms, therefore, is an essential skill of building a quality Rails application.
In this guide, we will walk through building a form from ‘scratch’ using HTML. There are a few learning outcomes to this exercise that will benefit you as you progress in your Rails journey:
- Become familiar with the MVC model & file structure Rails uses
- Setting up a rails app (mostly) manually for creating a new user
- Ensuring Forms are filled out properly
- Setting up a simple database, aka ‘model’
- Creating and adding the appropriate
- Authenticating our form in Rails using
presence
Note that Rails makes creating many of these files extremely simple using helper methods. In some ways … it’s too easy, and so going through the process manually will help us gain a better understanding of what ingredients all come together to make a delicious Rails cake … er… app.
Pre-requisites
- I am assuming your have minimal experience creating a Rails app — If, not don’t worry too much as you can still follow along if you’ve successfully installed Ruby and Rails
- There are loads of guides out there to get that set up
- If you’ve created a Rails app before, you’ve used your computers terminal application and can’t navigate folders on the command line.
- Some familiarity with HTML is helpful
Versions I’m using at the time of writing
- Ruby v. 3.0.0
- Rails v. 6.1.3.1
- MacOS 11
Part One: Manually Set Up Our App
Get Set Up: Generate a new rails app
Let’s create a new Rails app for our form projects called “formidable”
- In your terminal application, navigate to the folder where you want to create your project and and enter
rails new formidable
and watch the lines of code whizz by as rails builds your project - If you want to keep track of what you are doing (and the ability to rollback to a version of your project before you are met with some seemingly irreversible ugly red error messages) now is a good time to set up your project with git
- In your terminal, navigate (
cd
) into your new application ➝cd formidable
- Open your project in your code editor. I’m using VS Code, so I’ll open my project from the command line using
code .
. You can also use the graphical user interface if that make more sense to you.
Create a “User” Model
Ok, now we want to start working with our database to get our model set up to take in and output information. Luckily we don’t need years of experience using SQL to get this going. The process in Rails is pretty straight forward.
Note Rails like shortcuts when typing commands in the command line — we will use the generate
command a few times. I like shortcuts and Rails lets us use g
in place of generate, so I will use that. Just remember what it means and that it does the same thing.
- In your terminal, enter
rails g model User username:string email:string password:string
- Ok. Nice! You will see a few lines in your terminal …
invoke
and somecreate
lines that tell us our command told rails to create some new stuff. - In your code editor, go and look at what was created. We built our first migration when we ran that command. The migration is essentially the file that communicates to the database what should go into it and how.
- Head to this path in your code editor to view the file ➝
db/migrate/
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :password
t.timestamps
end
end
end
This is what my file looks like. Note the part that says create_table
followed by a few bits of input — Aha! There is the stuff we defined in our terminal command — :username
, :email
, and :password
- Ok, that migration file is what we want to tell our database to look like. We didn’t actually send the data to the database yet … so … does that mean it’s just … base? Get it?
Ok, let’s use this migration file and … well, migrate it to the database ➝ in terminal enterrails db:migrate
- If you’re curious go back to yourmigrate
folder in your code editor and you will now see aschema.rb
- don’t edit that guy. We will stick to editing the database with ‘migrations’.
Add Form Validations
It’s likely that you are going to want to restrict what kinds of content your users send to the database. You don’t really want a user submitting a blank field. We want to make sure there is something filled out for ‘username’ and probably want to make sure they’ve set up a password. Let do that
- In your editor, go to
app/models/user.rb
It should be pretty bare bones. Edit this file to look like this:
class User < ApplicationRecord
validates :username, :email, :password, presence: true
end
This little edit in our model says to Rails, “hey, check (validate) these things to make sure they exist when someone submits the form.”
Controller: generate our first controller
We’ve already tackled the M in the Rails MVC structure ➝ the ‘model’ or database. Let’s knock out the C part now. We need to tell Rails what to do with those bits we fed the User model we created. For now, we simply create an ‘empty method’ for each action we’d like to perform — in this case :create
and :new
- Head over to
app/controllers/user_controllers.rb
. It will look like this:
class UserController < ApplicationController
end
Not much going on here. Let add our ‘empty method’. Why ‘empty’ — well because we aren’t going to write anything inside these method (yet). We are just telling the controller “hey, these things exist so when you do stuff, just know they are there, mmmk?” Change this file to this:
class UserController < ApplicationController
def new
end
def create
end
end
Easy. Now Rails knows that these exist and we are a step closer to getting to the real fun bits. The FORM!!! But first, we need to make sure any request to use “new” or “create” are routed to the correct path. Otherwise … well .. errors.
Routes
We need to be able to properly direct any incoming request. In our case, we are concerned with “creating” a new user. Let give our User model the ability to receive a create
and new
request. “Hey, aren’t those the same things?” Sort of, and sort of not.
That could take us into the weeds a bit, but the simple answer is that new
creates an new object instance and create
is necessary for saving it to the database.
A quick note about ‘resource’ — They are essentially all the actions we can perform using our forms following the CRUD principles (Create, Read, Update, Destroy). This includes things like making a new user, editing the user or content, deleting, the user, viewing the user info etc.
- Head to the
routes.rb
file to edit the available methods for our form ➝config/routes.rb
Add the resource line for our user model
Rails.application.routes.draw do
resources :users, only: [:new, :create]
end
View — Lets see what we’ve got!
Ok, we won’t actually see anything yet. We need to start building some forms before we see anything. For now let just create our first view (aka page) and put a little something there so we know everything is working.
- In your editor, go to
app/views/users
. Right click on the users folder and create a new file callednew.html.erb
- Let’s enter a title to render so we can see something on our newly minted page ➝
<h1> This Site is Formidable! </h1>
- The moment of truth. In your terminal, start up your rails server by running
rails server
orrails s
(there’s those sweet shortcuts again). - The output should say it started your server. Go this this url in your browser:
http://localhost:3000/users/new
or http://127.0.0.1:3000/users/new will get you to the same place. Boom! You should see your title appear.
Part Two — Building HTML Forms
This is the part I promised you from the beginning. It is supposed to serve as a Form refresher and to deepen your understanding of how forms make Rails applications tick. Put on your HTML hat and let’s build some ugly but useful forms. Oh, did I mention that what we are making is ugly? Yeah, no CSS here. I’d love to pretty them up but this guide is long enough as it is.
Remember, rails has helpers to do most of this stuff for us, but we are doing it manually with the goal of being awesome … at forms.
Manually Create a New User Form
As we build our our form, we need to keep a few things frontmost in our mind when working in Rails — method
and action
but also name
. This one (name
) is of particular importance as we will see later.
- Open your
<form>
tag and enter inputs for each of our model attributes -username
,email
andpassword
. - Remember the action and method on the form tag and to make the password type
type='password
. - Before copying what I’ve got below, see if you can create the form. Don’t forget the submit input so our users have a button to mash when they are ready.
…………
Ok, you should have something in your app/views/new.html.erb
file that looks like this:
<form action='/users' method='post' accept-charset="UTF-8">
<label for='username'>Username<label>
<input type="text" id="username" name="username"> <br>
<label for="email">Email Address</label>
<input type="text" id="email" name="email" > <br>
<label for="password">Password</label>
<input type="password" id="password" name="password"> <br>
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token%>" >
<input type="submit" value="Submit">
</form>
If you got that, then pat yourself on the back and have a beer. If not, don’t worry. I didn’t supply you with some key information.
Action & Method one the <form>
tag
The action='/users'
and method='post'
can be found if you run rails routes
in your terminal. It will return a whole bunch of different routes. Since we generated our controller and added the :new
and :create
resources we should see a list of available routes that correspond to those
It will list the appropriate path and method for each.
Authenticate
The other thing I didn’t mention was the hidden input that hides the authenticity token. This is important for security and ensuring your app doesn’t have bad or malicious data submitted, and only data from your proper form comes through.
- This is the only embedded ruby code in the file so far. Take a look at the code block above and note the
value
of the hidden input ➝<%= form_authenticity_token%
Fd Rails supplies us with this token and we just need to implement it here
If you have done everything correctly your form should submit. You can tell by looking at the terminal output when you click the submit button. It should look like this:
Started POST "/users" for 127.0.0.1 at 2021-04-06 12:37:59 -0400
Processing by UsersController#create as HTML
Parameters: {"username"=>"bryan", "email"=>"bryangoertz@gmail.com", "password"=>"[FILTERED]", "authenticity_token"=>"[FILTERED]"}
No template found for UsersController#create, rendering head :no_content
Completed 204 No Content in 1ms (Allocations: 325)
Cool, it has all our submitted info! That’s awesome, but wait, what is this “No template found issue?
We successfully submitted our form but we didn’t tell Rails what should happen after that.
We have a new user, but we haven’t saved it to the database yet. Let’s build the #create
action to take care of the next steps for us:
- Go to
UsersController
in theapp/controllers
directory where we have our two blank methods for#create
and#new
- It’s time to add a bit of logic to our create method. The goal is for a successful user creation to redirect back to the new user form, blank and ready for another new user creation. If it fails for some reason, we want to re-render the same
:new
form with the existing info in it - There was an error and we can build in error messages later. - Our
users_controller
should now look like this
# app/controller/user_controller.rb
def new
end
def create
@user = User.new(username: params[:username], email: params[:email], password: params[:password])
if @user.save
redirect_to new_user_path
else
render: :new
end
end
If all was done correctly our server terminal should output some info about some kind of INSERT INTO "users" ...
blah blah blah. That’s good! It’s an SQL command that inserted our data into the table.
- Lets simplify our params argument in the
create
method - it’s a bit unruly. If we were using the helper methodform_with
we’d have a hash of attributes for user parameters stored for us
This is where thename
attribute in our forms comes into play - Nest your user fields (username, email, password) inside the
name
attribute using hard brackets ➝ for examplename="user[email]"
- Do that now. Go to your
app/views/new.html.erb
file and change your formsname
attribute for each field appropriately. It should look like this when you are done:
<form action='/users' method='post' accept-charset="UTF-8">
<label for="username">Username</label>
<input type="text" id="username" name="user[username]"> <br>
<label for="email">Email Address</label>
<input type="text" id="email" name="user[email]" > <br>
<label for="password">Password</label>
<input type="password" id="password" name="user[password]"> <br>
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token%>" >
<input type="submit" value="Submit">
</form>
Ok Great! Now let’s backtrack a bit.
- Go back into your
app/controllers/users_controller.rb
and comment out your new user line in the create method. It’s the one that start with the user instance variable@user ...
and has all our individualparams
in it. - Create a
private
method for our params. Security!!
Go to the bottom of the file just above the lastend
and add the following code that will do the work of the line we just commented out, but securely usingpermit
andrequire
:
...
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
Now we need to tell our :new
method where it can find the params in the private
area. We do that by passing the new private method into the new
method where our params used to live before we commented them out.
- Go to your commented out line above and change to this:
- @user = User.new(user_params)
- The whole
users_controller.rb
will now look like this:
class UsersController < ApplicationController
def new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to new_user_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
Ok.. that’s basically all we need. Go ahead and submit a new user to the database and take note of the output.
I don’t know if you noticed, but before when we submitted our user data, it puked back all our info like it was giving away free pizza. Your name, email and password were laid out to bare for anyone to peep leaving you vulnerable, scared and possibly the victim of some malicious activity.
Now with our private method passed into our :new
method our information is cloaked and secure. And that is what we want!
The best part about everything we just learned is that we should know it, but don’t reallllly need to write it all out if we opt to use the form_with
helper method.
Stay tuned for a guide on using helper methods in Rails forms. They’re tons of fun.
TL;DR ➝ Here is a quick step by step list of what we did above if you just want the steps:
Part One
rails new formidable
- Initiate git and start controlling your versions
- Move into your project folder
cd formidable
- Open in VS Code
code .
Part Two — Create User Model & Form Validation
- In terminal, generate a new database model by using the command
rails g model User username:string email:string password:string
- Navigate to the migration file that was just created to ensure it was created properly —
db/migrations/crazynumberfile
If it’s correct, move on. If not , delete it and retry. - Run
rails db:migrate
- Navigate to
app/models/users.rb
and add your validations. Inside the rubyclass
addvalidates :username, :email, :password, presence: true
Part Three — Generate Controller and Add Route Resources
- Add our methods to the UserController. We are only working with creating a user for this guide, so we will add
#new
and#create
actions. Inapp/controllers/users_controller.rb
- they will be left empty for now. - Your
users_controller.rb
file should look like this
class UserController < ApplicationController
def new
end
def create
end
end
- Tell rails where to go when a
new
orcreate
request is made: in yourconfig/routes.rb
add the resources we want to grant our user object.
Rails.application.routes.draw do
resources :users, only: [:new, :create]
end
Part Four — View and Forms Time
- Inside the
app/views/users
directory create a file callednew.html.erb
- Add an
<h1>
or some other content to make sure your page is rendering. - Fire up your server —
rails s
- Navigate your browser to your “new” page
localhost:3000/users/new
and you should see your newly created test content - Create your form with fields and label for our 3 database / model items —
username
,email
andpassword
- be sure to give your form an action (path) and method (for submitting a form we use POST). - Add in a rails authenticity token to build up security for our form using the Rails provided
<%= form_authenticity_token %>
- we put this in a hidden input. See full code block after this section for reference. - Because our controller takes over some of the process of form submission we need to give our fields the appropriate
name
attributes that include our model name and proper attributes ➝ eg.name="user[email]"
. Do this for all form fields that need a name. Theauthenticity_token
andsubmit
inputs won’t follow this formatting. - Your full form should look like this:
<h1>This App is Formidable! </h1>
<form action='/users' method='post' accept-charset="UTF-8">
<label for="username">Username</label>
<input type="text" id="username" name="user[username]"> <br>
<label for="email">Email Address</label>
<input type="text" id="email" name="user[email]" > <br>
<label for="password">Password</label>
<input type="password" id="password" name="user[password]"> <br>
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token%>" >
<input type="submit" value="Submit">
</form>
- Go to
UsersController
in theapp/controllers
directory where we have our two blank methods for#create
and#new
- We want to create user instances using
@user = User.new
followed by the parameters we are looking for but we need to keep our data private. So instead of passing in our params directly in thecreate
method after@user = User.new
lets add aprivate
method just above the finalend
that looks like this:
...
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
- We can now pass this
user_params
method into our#create
action above with the result that ourparams
(i.e. what we permit) is kept secure. Theusers_controller.rb
will look like this:
class UsersController < ApplicationController
def new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to new_user_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
- Note that we added some conditional logic to redirect to the appropriate page on success or error.
That’s the short version. If you get stuck there, have a look about where I go through the process in greater detail.
Party!