A Rails View is an ERB program that shares data with controllers through mutually accessible variables. Rails uses ERB (Embedded Ruby) as its default template engine to interpret and process .html.erb files.
If you look in the app/views directory of any Rails application, you will see one subdirectory for each of the controllers. Each of these subdirectories was created automatically when the same-named controller was created with the generate script.
Rails lets you know that you need to create the view file for each new method. Each method you define in the controller needs to have a corresponding erb file, with the same name as the action (method), to display the data that the method is collecting.
Assume that a controller is created with the following command:
rails generate controller Book
This will create the controller (app/controllers/books_controller.rb). If you define an action inside this controller:
defindexend
You need to create the view file as app/views/books/index.html.erb.
ERB (Embedded Ruby) allows you to write Ruby code inside HTML using special tags:
ERB Syntax
Purpose
<% … %>
Executes Ruby code (no output)
<%= … %>
Executes and outputs the result
<%# … %>
Comment (ignored in output)
So, let’s create view files for all the methods we have defined in the books_controller.rb. While executing these views, simultaneously check these actions are applicable into the database or not.
Creating View File for create Action
Modify the books_controller.rb file to add following action methods:
defnew@book=Book.newend
Here, @book is an instance variable. The new action initializes a new Book and used in app/views/books/new.html.erb to build the form and enter Book details. (We shall see how to create the form).
Also define create action to handle the form submission. The form data is submitted to this action which calls save method of the book instance and pushes a flash message indicating the success.
flash[:notice]="Book was successfully created."
redirect_to @book# Redirects to show action (book details)else
render :new, status::unprocessable_entityendend</pre>
Rails form Helpers
Rails recommends using its helpers instead of using the normal HTML elements to build the content tags and form elements. The following code in new.html.erb uses form helpers like form_with, form.label, form.text_field etc. to render a blank Book form.
Following helpers are used inside the form helper form_with, for example −
<%= form_with(model: @book, local: true) do |f| %>
<%= f.label :title%>
<%= f.text_field :title %><%= f.submit "Save"%>
<% end %>
Form element helpers
label : renders a HTML label
text_field : renders a text input element, equivalent to <input type= "text">
text_area : generates a textarea element
radio_button : Rails helper for <input type= "radio">
check_box : used to render a check box, as done by <input type="checkbox">
submit : outputs an <input type= "submit"> button that submits the form.
URL & Link Helpers
link_to : creates a hyperlink, equivalent to href
button_to : generates a form-based button
image_tag : Inserts an image, similar to img src
Make sure that the config/routes.rb file is present with following resources statement.
resources :books
Start the Rails server
rails server
Add New Book
Visit http://localhost:3000/books/new URL to display the form.
Enter data for a new book and click the Create Book button.
Creating View File for list of books
The index action in the BooksController stores all the objects in the Book model in @books variable.
defindex@books=Book.all
end
This will need the index.html.erb file. The book data is displayed in a HTML table. In addition to the columns title, author and price, we use link_to helper to hyperlink buttons for show, edit and delete actions are provided in each row.
We also use the flash[:notice] helper to extract the flash messages pushed by any of the earlier actions.
While on the index page that shows the list of books, click on the edit button to update the details. Change the desired data and click on Update book button. The browser returns the list of books with a flash message âBook was successfully Updatedâ
Finally, the delete action in the BooksController calls the destroy() method on the selected Book object and removes from the book model.
The routing module provides URL rewriting in native Ruby. It’s a way to redirect incoming requests to controllers and actions. It replaces the mod_rewrite rules. Best of all, Rails’ Routing works with any web server. Routes are defined in app/config/routes.rb.
A route is the part of the URL that determines how an incoming HTTP request is directed to the appropriate controller and action for processing. It tells Rails which controller and action should respond to a request.
Think of creating routes as drawing a map for your requests. The map tells them where to go based on some predefined pattern −
Rails.application.routes.draw doPattern1 tells some request to go to one place
Pattern2 tell them to go to another
...end
When Rails sees a request that matches any of the patterns, it will send the request to the corresponding controller and the appropriate action inside of that controller.
There are 4 common actions you will generally need for a resource: Create, Read, Update, Delete (CRUD). This translates to 7 typical routes:
Action
HTTP Verb
Path
Purpose
index
GET
/books
List all records
show
GET
/books/:id
Show a single record
new
GET
/books/new
Display a form for a new record
create
POST
/books
Save a new record to the database
edit
GET
/books/:id/edit
Display a form to edit a record
update
PATCH/PUT
/books/:id
Update an existing record
destroy
DELETE
/books/:id
Delete a record
Typing out these routes every time is redundant, so Rails provides a shortcut for defining them. To create all of the same CRUD routes, replace the above routes with this single line.
Example
Let us consider our library management application contains a controller called BookController. We have to define the routes for those actions which are defined as methods in the BookController class.
Open routes.rb file in library/config/ directory and edit it with the following content.
Rails.application.routes.draw do
resources :booksend
If you decide to define only a certain routes instead of all the default routes, you can put the required routes in a list.
Rails.application.routes.draw do
resources :books, only:%i[index show create]end
You may also define the routes explicitly by mentioning the respective HTTP verbs
Rails.application.routes.draw do
get 'books/'
get 'books/show'
post 'books/create'end
This works because Rails automatically maps get ‘books/index’ to BooksController#index.
If the path does not directly match the controller#action, you must specify to: explicitly:
get 'all-books', to:'books#index'# Custom URL: /all-books
get 'book-view/:id', to:'books#show'# Custom URL: /book-view/:id
If your application has multiple models (e.g., Book and Employee), you should have separate resources statements for each model in config/routes.rb.
Rails.application.routes.draw do
resources :books
resources :employeesend
The routes.rb file defines the actions available in the applications and the type of action such as get, post, and patch.
Use the following command to list all your defined routes, which are useful for tracking down routing problems in your application, or giving you a good overview of the URLs in an application you’re trying to get familiar with.
rails routes
In the older version of Ruby on Rails (before version 5), you would use:
rake routes
What is Next?
Next, we will create the code to generate screens to display data and to take input from the user.
The library application used in this tutorial has the CRUD actions to add, edit, and delete book objects. However, these actions are accessible to anyone, which isn’t safe. Let us add a security layer to the application, so that only authenticated users get the access.
Authentication Generator
Starting with Rails version 8.0, a default authentication generator is included to streamline the process of securing your application by allowing access only to verified users.
From the command terminal (windows) or Linux terminal, run the following command:
Assuming that you have already created the Bookscontroller class, defined an index action in it and provided the corresponding index.html.erb file.
Run the Rails server and visit http://localhost:3000/ upon which the browser displays this login page:
Rails authenticates the entered email address and password registered with it and automatically redirects the browser to the index view.
By default, the Rails authentication generator will restrict all pages to authenticated users only. However, if you want some of the pages to be accessible without authentication, such as guest visitors, allow_unauthenticated_access property can be used for the purpose.
How to Allow Guest Users
To allow guests to view products, we can allow unauthenticated access in our controller.
classBooksController<ApplicationController
allow_unauthenticated_access only:%i[ index show ]# ...end
Rails also provides the before_action that runs before executing a specific controller action. It’s mainly used for tasks like authentication, setting up variables, or checking permissions.
The before_action callback helps avoid redundant code (e.g., authentication checks in every action). Use only: and except: inside the controller to control execution.
There is also an after_action callback, intended to run after the action runs, and the round_action callback that wraps before & after the logic.
This built-in authentication system works well for small applications. You can use Devise Authentication by using Devise (a popular authentication gem). To generate authentication with Devise:
rails generate devise:install
rails generate devise User# Generates User model with authentication
rails db:migrate
You can also use jwt for token-based authentication.
The Ruby on Rails framework follows the MVC pattern that implements REST architecture. One of the important constraints of REST is statelessness. It implies that each request sent by the client must have enough information for the server to handle and satisfy it, and that the server doesnât hold on to the request data once it forms the response and disconnects from the client.
What are Cookies?
In some situations web applications need to maintain state between multiple requests. When a user successfully logs in, they shouldn’t be asked to log in again when accessing another resource. Rails (in general the applications implementing REST) uses cookies to store data between multiple requests.
Cookies are small pieces of data stored in the user’s browser. Rails allows you to set, read, and delete cookies. The cookies method in Rails acts as a hash.
Sessions are a wrapper over cookies. Rails encrypts and signs to store more sensitive user-related data securely. Rails stores session data in a cookie by default using ActionDispatch::Session::CookieStore.
Flash messages in Rails are built on top of the session object. They are used to pass temporary one-time messages (like success or error alerts) from one request to the next.
Rails provides a flash hash that’s stored in the session. Flash uses the session behind the scenes −
It stores the flash hash in session[‘flash’].
On the next request, Rails clears the flash data automatically (unless told otherwise).
You can render the following types of flash messages −
Flash Key
Purpose
Display Color
:notice
For general success messages
Green or Blue
:alert
For warnings or errors
Red
:error
Custom â often for errors
Red
:success
Custom â for clear success
Green
Examples −
flash[:notice]="Successfully logged in"
flash[:alert]="Invalid username or password"
flash[:error]="Something went wrong!"
flash[:success]="Your profile has been updated"
Example: Cookies, Sessions, and Flash Messages
Let us put the concept of cookies, session and flash in the following example.
Start by creating a new Rails application −
rails new sessiondemo
cd sessiondemo
Add a User model and corresponding controller and views by generating the scaffold.
rails generate scaffold User name:string email:string password:string
rails db:migrate
You need to create a Sessions Controller for handling Login/Logout operations
Add the login and logout routes by editing the routes.rb file in the app/config folder.
resources :users
get 'login', to:'sessions#new'
post 'login', to:'sessions#create'
delete 'logout', to:'sessions#destroy'
root "users#index"
The SessionController class defines the methods for performing Session activities. The new action sets up a cookie as the email of logged user.
When a user logs in, a new session is created. It also flashes a message letting the user know that the login is successful. The destroy action removes the session variable.
In app/controllers/sessions_controller.rb −
classSessionsController<ApplicationControllerdefnew@remembered_email= cookies[:remembered_email]enddefcreate
user =User.find_by(email: params[:email])if user && user.password == params[:password]
session[:user_id]= user.id
flash[:notice]="Welcome, #{user.name}!"if params[:remember_me]=="1"
<p>Logged in as <%=User.find(session[:user_id]).name %> |
<%= button_to "Logout", logout_path, method: :delete, data:
{ confirm: "Are you sure you want to log out?" } %></p><%else%>
The Rails controller is the logical centre of your application. It coordinates the interaction between the user, the views, and the model. The controller is also a home to a number of important ancillary services.
It is responsible for routing external requests to internal actions. It handles people-friendly URLs extremely well.
It manages caching, which can give applications orders-of-magnitude performance boosts.
It manages helper modules, which extend the capabilities of the view templates without bulking up their code.
It manages sessions, giving users the impression of an ongoing interaction with our applications.
The following diagram explains how the controller interacts with the model and the view layer −
The process for creating a controller is very easy, and it’s similar to the process we’ve already used for creating a model.
Make sure that you have already created the book model and performed migrations
rails generate model Book title:string author:string price:integer
rails db:migrate
Notice that you are capitalizing Book and using the singular form. This is a Rails paradigm that you should follow each time you create a controller.
This command accomplishes several tasks, of which the following are relevant here −
It creates a file called app/controllers/book_controller.rb. If you look at book_controller.rb, you will find it as follows −
classBooksController<ApplicationControllerend
Controller classes inherit from ApplicationController, which is the other file in the controllers folder: application.rb.
The ApplicationController contains code that can be run in all your controllers and it inherits from Rails ActionController::Base class.
In the MVC architecture, a route maps a request to a controller action. A controller action performs the necessary work to handle the request, and prepares any data for the view. A view displays data in a desired format.
We need to define actions in the BooksController class to handle the requests. Actions are instance methods in the class. Note that it is upto you what name you want to give to these action methods, but better to give relevant names.
By default, the rails generate controller command creates the controller file (books_controller.rb) but does not include any actions unless explicitly specified. Let us define index(), show() and create() actions in the BooksController class.
Note that you can generate a controller with specific actions (e.g., index, show, create, etc.), if you include them in the command:
rails generate controller Books index show create
Implementing the create Method
The create action is expected to be invoked by the router in response to POST /books request. It creates a new book object and returns the JSON response of newly created object.
The first line creates a new instance variable called @book that holds a Book object built from the data, the user submitted. The book_params method is used to collect all the fields from object :books. The data was passed from the new method to create using the params object.
The next line is a conditional statement that renders the book data in JSON if the object saves correctly to the database. If it doesn't save, the user is sent back error message.
Implementing the show Method
The show action displays the details of a book object of a certain ID passed by GET /books/:id request.
defshow@book=Book.find(params[:id])
render json:@bookend</pre>
The show method's @book = Book.find(params[:id]) line tells Rails to find only the book that has the id defined in params[:id].
The params object is a container that enables you to pass values between method calls. For example, when you're on the page called by the list method, you can click a link for a specific book, and it passes the id of that book via the params object so that show can find the specific book.
Implementing the index Method
The index method gives you a list of all the books in the database. This functionality will be achieved by the following lines of code. Edit the following lines in book_controller.rb file.
defindex@books=Book.all
render json:@booksend</pre>
The books_controller.rb file has the code as follows −
Note that the book_params method defined above is a private method in the controller that ensures only the allowed attributes of a Book record are accepted from user input. It prevents mass assignment vulnerabilities, which could allow a user to update fields they shouldn’t have access to.
Rails also generates config/routes.rb file. Modify it to define RESTful routes:
Rails.application.routes.draw do
resources :booksend
You will learn more about routes in Rails in one of the subsequent chapters.
Test the Controller
To verify if the actions defined in BooksController work as desired, start your Rails server:
rails server
Use the Postman tool to test endpoints of your application:
POST http://localhost:3000/books â to create a new book
Set the Content-Type header application/json and enter the following JSON expression as body:
{"book":{"title":"FUNDAMENTALS OF COMPUTERS","author":"Rajaraman","price":475}}
Execute the POST action. This adds a new record in the books table:
GET/books/:1 â Fetch a specific book with id=1
Postman's response pane will display the details of specified book:
GET/books → list all the books in the model
The responses pane displays the JSON representation of all the books available.
You can also implement the UPDATE and DELETE operations in the BooksController by adding these action methods.
In the earlier chapters of this tutorial, we explained the Active Record ORM in Ruby on Rails framework. Active Record is an ORM (Object Relational Mapper) that connects objects whose data requires persistent storage to a relational database. One of the functionality of Active Record is to form Active Model. In this chapter, we will explore the features of Rails Active Model.
Relation between Active Model and Active Record
The relation between Active Model and Active Record can be stated as follows −
Active Record includes Active Model.
Every Active Record object has all the capabilities of Active Model plus persistence.
Hence, you will use the Active Record when you need to persist data in a database, whereas you need to use the Active Model when you need form-like models (e.g., ContactForm, SearchForm, Payment) that donât need to be stored in the database.
Rails Active Model Features
Active Model is a library containing various modules that presents a set of interfaces and minimal functionalities for building model-like classes without having to use a database.
Features of Active Model include −
Validations (validates)
Callbacks (before_validation, etc.)
Naming and conversion (e.g., model_name)
Serialization (e.g., to_json, to_xml)
Error handling (errors.add, errors.full_messages)
Dirty tracking (attribute_changed?)
Note − Active Model allows non-persistent Ruby objects (i.e., not backed by a DB) to behave like Active Record objects in terms of form handling, validation, and serialization.
Here are some more features of Rails Active Model −
ActiveModel::API to interact with Action Pack and Action View by default, and is the recommended approach to implement model-like Ruby classes.
The Active Model class has Attributes with which you can define data types, set default values. To use Attributes, include the module in your model class and define your attributes.
Inside the Active Model class, you may have one or more callback functions to hook into model lifecycle events, such as before_update and after_create.
ActiveModel::Serialization provides basic serialization for your object. An attribute hash (must be strings, not symbols) hash should contain the attributes you want to serialize.
Another feature is ActiveModel::Validations that adds the ability to validate objects and it is important for ensuring data integrity and consistency within your application.
Example: Active Model in Rails Application
Let us use Active Model in the following Rails application. To start with, create an application and declare a Book scaffold as below −
rails new myapp
cd myapp
rails generate scaffold Book title:string author:string
rails db:migrate
Create the Active Model
Create a forms folder under the app folder in your project directory and declare the BookForm class acting as the Active Model −
Rails has already created a view as new.html.erb file. Open it and edit the same to include the following code −
<%= form_with model: @book_form, url: books_path, local: true do |form| %>
<% if @book_form.errors.any? %>
<div id="error_explanation"><h2><%= pluralize(@book_form.errors.count, "error") %>
prohibited this book from being saved:</h2>
<ul>
<% @book_form.errors.full_messages.each do |msg| %>
<li><%= msg %></li><%end%>
</ul></div><%end%>
Thatâs it. Save the changes and run the Rails server. Visit http://localhost:3000/books/new to display a New Book form.
Try clicking the Create Book button without entering any data −
The error messages will be flashed as below −
This time around, enter the title and author fields and submit the data. Rails will display a confirmation as below −
In this example, we used the Book model (Active Record) which is mapped to the books table in the database. On the other hand, BookForm is the Active Model object. The attributes of the Active Model are not stored persistently in the database. It is used for validation and abstraction.
Rails Migration allows you to use Ruby to define changes to your database schema, making it possible to use a version control system to keep things synchronized with the actual code. Instead of writing schema modifications in pure SQL, you can use a Ruby Domain Specific Language (DSL) to make required changes to your table structure.
This has many uses, including –
Teams of Developers − If one person makes a schema change, the other developers just need to update, and run “rake migrate”.
Production Servers − Run “rake migrate” when you roll out a new release to bring the database up to date as well.
Multiple Machines − If you develop on both a desktop and a laptop, or in more than one location, migrations can help you keep them all synchronized.
Migration acts as a new ‘version’ of the database. When you generate a model, a schema has nothing in it. Each migration modifies it to add or remove tables, columns, or indexes.
Migrations support all the basic data types − The following is the list of data types that migration supports −
string − for small data types such as a title.
text − for longer pieces of textual data, such as the description.
integer − for whole numbers.
float − for decimals.
datetime and timestamp − store the date and time into a column.
date and time − store either the date only or time only.
binary − for storing data such as images, audio, or movies.
Boolean − for storing true or false values.
Valid column options are − The following is the list of valid column options.
comment − Adds a comment for the column.
collation − Specifies the collation for a string or text column.
default − Allows to set a default value on the column. Use nil for NULL. (:default => “blah”)
limit − Sets the maximum number of characters for a string column and the maximum number of bytes for text/binary/integer columns. (:limit => “50”)
null − Allows or disallows NULL values in the column. (:null => false)
precision − Specifies the precision for decimal/numeric/datetime/time columns.
scale − specifies the scale for the decimal and numeric columns.
Create the Migrations
In the previous chapter, you have created Book and Subject models with the following commands −
malhar@ubuntu:~/library$ rails generate model Book title:string price:float subject:references
malhar@ubuntu:~/library$ rails generate model Subject name:string
Note that using subject:references in the Book model automatically creates a subject_id foreign key column in the books table.
Migration files are created inside the db/migrate folder, one for each migration class. The name of the file is of the form YYYYMMDDHHMMSS_create_books.rb, which has the following code −
Similarly, there is a has_many relationship between Book and subjects (subject.rb)
classSubject<ApplicationRecord
has_many :booksend
NOTE − Before running the migration generator, it is recommended to clean the existing migrations generated by model generators.
The db:migrate command
Now that you have created all the required migration files. It is time to execute them against the database. To do this, go to a command prompt and go to the library directory in which the application is located, and then run the rails migrate command as follows −
This command is used to modify the structure of an existing table, such as adding or removing a column.
Adding Columns
When you want to add a new column to an existing table in your database, you can use a migration with the format "AddColumnToTable" followed by a list of column names and types.
To add a new column named author in the book model, use the following command −
Whenever a model structure is altered, the migration command defines the change() method to perform corresponding action (add_column, remove_column etc). It supports the following actions −
add_column
add_foreign_key
add_index
add_reference
create_join_table
create_table
drop_table
remove_column
remove_foreign_key
remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table
You can also use the up and down methods instead of the change method (change method was introduced in Rails 5.0, and is the standard way to modify the migrations, although up/down methods are still supported but not recommended).
The method up is used when migrating to a new version, down is used to roll back any changes if needed.
Other Migration Commands
In addition to the rails db:migrate command, the following migration commands are useful.
Rolling Back
If you have made a mistake in the model definition, rather than tracking down the version number associated with the previous migration you can run:
malhar@ubuntu:~/library$ rails db:rollback
This will roll back the ldatabase to the latest migration, either by reverting the change method or by running the down method. If you need to undo several migrations you can provide a STEP parameter:
malhar@ubuntu:~/library$ rails db:rollbackSTEP=2
Setting Up the Database
The rails db:setup command will create the database, load the schema, and initialize it with the seed data.
Resetting the Database
The rails db:reset command will drop the database and set it up again. This is equivalent to rails db:drop db:setup command.
Read this chapter to learn the different ways to retrieve data from the database using Active Record.
You generally need to execute the SELECT query in raw SQL to retrieve data from the tables in a database of any type (such as MySQL, PostgreSQL, Oracle SQLite etc.). In most cases, the Rails ORM implemented by Active Record insulates you from the need to use raw SQL.
An Active Record helps you perform query operation in database-agnostic manner. So that, you don’t need to change the Rails code even if the database type is changed.
For this chapter, we shall use the employees table populated with a sample data.
First of all, generate the Employee model and migrate the same.
While in the Rails application directory, open the Rails Console and verify that the Employee model has the above data by calling the all method. Observer that it internally executes the SELECT query to fetch all records.
Rails Active Record query interface provides different methods to retrieve objects. Let us learn about the most frequently used methods.
The find() Method
With the find() method, you can retrieve the object corresponding to the specified primary key that matches any supplied options. For example, the following statement fetched the employee with id = 2
Rails will not apply any implicit ordering with the take method that retrieves a given record. Without any arguments, the very first record in the table is retrieved, as can be observed from the SQL query.
The first method finds the first record ordered by primary key. Accordingly, it adds the ORDER BY clause in the SELECT query internally executed by Rails.
You can also retrieve multiple objects. The find_each() method is a memory-friendly technique to retrieve a batch of records and then yields each record to the block individually as a model.
You can use the rails runner command to echo the records on the command terminal.
Here is another example. All the objects with last_name column having the given characters are returned. As in SQL, using LIKE keyword is defined in Rails Active Record class.
library(dev)> params ={ last_name:"Do"}=>{:last_name=>"Do"}
library(dev)>Employee.where("last_name LIKE ?", params[:last_name]+"%")EmployeeLoad(0.3ms)SELECT"employees".*FROM"employees"WHERE(last_name LIKE'Do%')
The order() Method
You know that the SELECT query has the ORDER BY clause. Active Record interface implements the same with its order() method. You need to pass the expression on which the query result should be ordered.
The following statement retrieves the employee objects on the ascending order of salary:
The Model.find method selects all the fields, which equivalent to SELECT * from table. To select only a subset of fields from the result set, you can specify the subset via the select method.
Relational databases have tables related to each other, and their relationship is established with primary and foreign keys. In Rails, the Active Record associations allow you to define relationships between models. Associations indicate how your models relate to each other. When you set up an association between models, Rails migration defines the Primary Key and Foreign Key relationships, ensuring the consistency and integrity of the database.
Rails supports six types of associations.
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
belongs_to Association
Consider a case of Book and Author models, where each book can be assigned to exactly one author. A belongs_to association reflects the relationship between the two.
Generate the models with the following commands −
rails generate model Author name:string
rails generate model Book title:string author:references
Rails generates the Book model with a foreign key column (author_id) and sets up the belongs_to association.
classBook<ApplicationRecord
belongs_to :authorend
The database server creates the tables books and authors with the relationship expressed by primary and foreign keys.
The belongs_to association means that this model’s table contains a column which represents a reference to another table. Use belongs_to in combination with a has_one or has_many to set up one-directional or bi-directional relationship.
has_one Association
A has_one association is useful when one other model has a reference to this model. That model can be fetched through this association. For example, if each supplier in your application has only one account.
Open the command prompt and use the following commands to generate the models −
rails generate model Supplier name:string
rails generate model Account account_number:string supplier:references
Edit the supplier.rb script and define the haas_one association with account model.
In the underlying database, the relationship between suppliers and accounts tables is reflected by the following figure −
The has_one association creates a one-to-one match with another model. says that the other class contains the foreign key. This relation can be bi-directional when used in combination with belongs_to on the other model.
has_many Association
A has_many association is similar to has_one, but indicates a one-to-many relationship with another model. This association indicates that each instance of the model has zero or more instances of another model.
This is how the has_many association is established between Author and multiple instances of Book model.
classAuthor<ApplicationRecord
has_many :booksend
The equivalent relationship diagram is as follows −
The has_many establishes a one-to-many relationship between models, allowing each instance of the declaring model (Author) to have multiple instances of the associated model (Book).
Unlike a has_one and belongs_to association, the name of the other model is pluralized when declaring a has_many association.
has_many :through Association
A has_many :through association is often used to set up a many-to-many relationship with another model through an intermediate model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model.
Consider a situation where a Doctor can have multiple Patients and a Patient can have multiple Doctors through Appointments, you can use a has_many :through association.
Generate the following models with these commands −
rails generate model Doctor name:string specialization:string
rails generate model Patient name:string age:integer
rails generate model Appointment doctor:references patient:references appointment_date:datetime
Now, create the associations. Edit the doctor.db script in app/models directory.
A has_one :through association sets up a one-to-one relationship with another model through an intermediary model. This association indicates that the declaring model can be matched with one instance of another model by proceeding through a third model.
Employee, Office, and Company
An Employee works in an Office.
Each Office belongs to a Company.
An Employee belongs to a Company through their Office.
If the models are set up as follows −
rails generate model Company name:string
rails generate model Office location:string company:references
rails generate model Employee name:string office:references
The Employee model is associated with Company with has_one:though type, and there’s a belongs_to association between Employee and Office models.
A has_and_belongs_to_many association creates a direct many-to-many relationship with another model, with no intervening model. This association indicates that each instance of the declaring model refers to zero or more instances of another model.
For example, consider an application with Assembly and Part models, where each assembly can contain many parts, and each part can be used in many assemblies.
Generate the models −
rails generate model Assembly name:string
rails generate model Part name:string
The has_and_belongs_to_many association creates a direct many-to-many association. Even though a has_and_belongs_to_many does not require an intervening model, it does require a separate table to establish the many-to-many relationship between the two models involved.
It is important that the state of an object of Active Record class is validated before saving the same in the underlying database. This is essential to ensure the integrity of the data. For example, your model might have email as one of the attributes. It must be ensured that the user enters a valid email address before saving the record.
Rails provides model-level validations to ensure that only valid data is saved into your database. There are many built-in helpers, and you can create your own validation methods as well.
Rails runs the validations before saving a new object or updating an existing object to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the INSERT or UPDATE operation.
Validation takes place when any of the following methods of Active Record are called:
create
create!
save
save!
update
update!
Methods with exclamation mark(!) at the end raise an exception when a record is invalid.
You can also run the save method by skipping the validation.
save(validate:false)
Let us first declare a Person model as below:
rails generate model Person name:string age: integer
If you declare an object as follows, and check if it is valid
library(dev)> p =Person.new(name:"Ravi", age:0)=>#<Person:0x0000026df640c228...
library(dev)> p.valid?=>false
The save method on invalid object returns false
library(dev)> p.save
=>false
If you try to skip the validation while running the save operation, Rails throws an error.
library(dev)> p.save!(library):4:in `<main>':Validation failed:Age must be greater than 0(ActiveRecord::RecordInvalid)
Validates Method
You can add validations to a model using the validates method inside the model class. This ensures that data meets certain conditions before being saved to the database.
Iin Rails, validates is a class method, provided by ActiveModel::Validations. It is used to declare validation rules for a model’s attributes.
*attributes − Takes one or more attribute names (like :title).
**options − Takes a hash of validation rules (presence: true, length: { minimum: 3 }, etc.).
Validation Helpers
There are many pre-defined validation helpers to be used inside your class definitions. Each helper accepts an arbitrary number of attribute names. The :on and :message options define when the validation should be run and what is the error message when the validation fails.
Presence Validation
This validation ensures that a particular field is not empty. In the above Person model, name cannot be empty.
This validation is commonly used to restrict the length of a string field. For example, if the Person model has a password field and you want to ensure it to be at least 8 characters long, you would write the following code −
This will ensure the age field is a number greater than or equal to 18.
Following options can be used to specify the validation rules:
:greater_than – Specifies the value must be greater than the supplied value.
:greater_than_or_equal_to – Specifies the value must be greater than or equal to the value.
:equal_to – Specifies the value must be equal to the supplied value.
:less_than – Specifies the value must be less than the supplied value.
:less_than_or_equal_to – Specifies the value must be less than or equal to the supplied value.
:other_than – Specifies the value must be other than the supplied value.
:in – Specifies the value must be in the supplied range.
:odd – Specifies the value must be an odd number.
:even – Specifies the value must be an even number.
:only_integer – specifies the value “must be an integer”.
Confirmation Validation
You should use this helper when you have two text fields that should receive exactly the same content. For example, the Person model has a field named as email and you want to confirm an email address.
Validation methods with older Rails syntax (before Rails 3) are still supported for backward compatibility. Examples:
validates_presence_of
validates_numericality_of
validates_length_of
validates_inclusion_of
validates_confirmation_of
Custom Validation
In addition to the built-in validation methods, Rails also allows you to create custom validations. For example, if you want to ensure that the name attribute in the Person model is always capitalized, you could write a custom validation method: