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

Bryan Goertz
Apr 7 · 13 min read

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.

  • 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
  • Ruby v. 3.0.0
  • Rails v. 6.1.3.1
  • MacOS 11

Let’s create a new Rails app for our form projects called “formidable”

  1. 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
  2. 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
  3. In your terminal, navigate ( cd ) into your new application ➝ cd formidable
  4. 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.

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.

  1. In your terminal, enter rails g model User username:string email:string password:string
  2. Ok. Nice! You will see a few lines in your terminal … invoke and some create lines that tell us our command told rails to create some new stuff.
  3. 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.
  4. 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

  1. 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 enter rails db:migrate - If you’re curious go back to your migrate folder in your code editor and you will now see a schema.rb - don’t edit that guy. We will stick to editing the database with ‘migrations’.

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

  1. 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.”

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

  1. 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.

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.

  1. 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

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.

  1. In your editor, go to app/views/users. Right click on the users folder and create a new file called new.html.erb
  2. Let’s enter a title to render so we can see something on our newly minted page ➝ <h1> This Site is Formidable! </h1>
  3. The moment of truth. In your terminal, start up your rails server by running rails server or rails s (there’s those sweet shortcuts again).
  4. 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.

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.

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.

  1. Open your <form> tag and enter inputs for each of our model attributes - username, email and password.
  2. Remember the action and method on the form tag and to make the password type type='password.
  3. 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.

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.

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:

  1. Go to UsersController in the app/controllers directory where we have our two blank methods for #create and #new
  2. 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.
  3. 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.

  1. Lets simplify our params argument in the create method - it’s a bit unruly. If we were using the helper method form_with we’d have a hash of attributes for user parameters stored for us
    This is where the name attribute in our forms comes into play
  2. Nest your user fields (username, email, password) inside the name attribute using hard brackets ➝ for example name="user[email]"
  3. Do that now. Go to your app/views/new.html.erb file and change your forms name 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.

  1. 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 individual params in it.
  2. Create a private method for our params. Security!!
    Go to the bottom of the file just above the last end and add the following code that will do the work of the line we just commented out, but securely using permit and require :
...
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.

  1. Go to your commented out line above and change to this:
  2. @user = User.new(user_params)
  3. 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.

Part One

  1. rails new formidable
  2. Initiate git and start controlling your versions
  3. Move into your project folder cd formidable
  4. 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 ruby class add validates :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. In app/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 or create request is made: in your config/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 called new.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 and password - 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. The authenticity_token and submit 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 the app/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 the create method after @user = User.new lets add a private method just above the final end 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 our params (i.e. what we permit) is kept secure. The users_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!

CodeX

Everything connected with Code & Tech!

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store