Recently I started answering questions at tetatail to deepen my understanding of rails. Looking at the questions there, some people may feel that they are missing basic knowledge when implementing CRUD, and it seems as if they were looking at themselves about a year ago, so it should be useful. I wrote this article. What is CRUD Also, by understanding this one CRUD, even when the number of models increases to two or three, it feels like applying based on this basic knowledge, so I think it is good to learn about CRUD of one model first. ..
--Those who want to learn about routing
--Those who do not know what params [: id]
refers to
――I was about a year ago
Describes the CRUD functionality of one model (model name: Post, column: title, content) in as much detail as possible. The description of the model is rarely mentioned in this article. The routing and controller will be the main. By the way, routing appears persistently. Also, please forgive that point because the code is also described as it is, where it is usually summarized.
% ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19]
% rails -v
Rails 6.0.3.2
% postgres --version
postgres (PostgreSQL) 9.6.18
Create an app with the rails new command. This time, the app name will be ʻone_model_crud`.
rails new [app name](one_model_crud) -d postgresql
Edit database.yml
database.yml
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV['PG_USERNAME'] %> #username set in postgresql
password: <%= ENV['PG_PASSWORD'] %> #password set in postgresql
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
% rails db:create
Created database 'one_model_crud_development'
Created database 'one_model_crud_test'
% rails s
Go to http: // localhost: 3000 /.
Although it deviates from the main subject, this time view uses slim, bootstrap for styling, and pry-rails as a debugging tool.
Now you are ready.
rails g model Create a model with the model name (singular).
% rails g model post
Add a title and content columns.
XXXX_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.0]
def change
create_table :posts do |t|
t.string :title
t.text :content
t.timestamps
end
end
end
Reflect the created database information.
% rails db:migrate
== 20200815005104 CreatePosts: migrating ======================================
-- create_table(:posts)
-> 0.0271s
== 20200815005104 CreatePosts: migrated (0.0272s) =============================
Let's check it on the console.
% rails c
irb(main):001:0> Post.new
=> #<Post id: nil, title: nil, content: nil, created_at: nil, updated_at: nil>
irb(main):002:0> Post.new(title: 'Title 1', content: 'Content 1').save
(0.3ms) BEGIN
Post Create (7.9ms) INSERT INTO "posts" ("title", "content", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["title", "Title 1"], ["content", "Content 1"], ["created_at", "2020-08-15 01:03:24.246072"], ["updated_at", "2020-08-15 01:03:24.246072"]]
(6.8ms) COMMIT
=> true
You have created a Post model. Now let's do this in the browser.
Set the routing to access posts # index
when accessing http: // localhost: 3000 /.
routes.rb
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
root to: "posts#index"
resources :posts
end
The resources: model (plural)
will generate the routing for the model's seven basic actions index, new, create, show, edit, update, and destroy.
You can also limit actions by writing resources: model (plural), only:: index
orresources: model (plural), only:% i [index]
.
Now let's look at the generated routing in the console.
% rails routes
Prefix Verb URI Pattern Controller#Action
root GET / posts#index
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
A routing like the one above has been created.
--Prefix
will be explained later.
--Verb
is an HTTP method.
--ʻURI Pattern points to the URL after http: // localhost: 3000. --
Controller # Action` refers to which action of which controller is performed when the URL is accessed.
With that in mind, let's take a look at http: // localhost: 3000 /.
prefix
rootVerb
GETURI Pattern
/Controller#Action
posts#indexThis means that when you visit http: // localhost: 3000 /, you will perform the index action on the posts controller.
Create a controller with the rails g controller model (plural)
.
% rails g controller posts
Running via Spring preloader in process 31151
create app/controllers/posts_controller.rb
invoke erb
create app/views/posts
invoke test_unit
create test/controllers/posts_controller_test.rb
invoke helper
create app/helpers/posts_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/posts.scss
Only ʻapp / controllers / posts_controller.rb and ʻapp / views / posts
are used, so you can delete them.
PostsController.rb
class PostsController < ApplicationController
def index
end
end
The contents will be set after the Post can be created.
Create index.slim in app / views / posts / and write the following
index.slim
h1 Posts#index
Now you are ready. Now actually access http: // localhost: 3000 /
% rails s
Flow when accessing
When you actually access it, the following screen will be displayed.
As an implementation flow
First, create a link to access the new registration screen. What you need here is ** Prefix **.
The link can be created by writing the following form.
= link_to'Character to display', Prefix_path
By writing like this, you can generate a link to access the URI Pattern of Prefix_path.
I will actually describe it.
index.slim
h1 Posts#index
= link_to 'sign up', new_post_path
This link is where Prefix
transitions to ʻURI Pattertn` in ** new_post **.
Let's take a look at the routing of the new action.
Prefix Verb URI Pattern Controller#Action
new_post GET /posts/new(.:format) posts#new
You have now created a link to access the new registration screen.
Click the new registration link and the URL will change to / posts / new. (* I get an error because I haven't defined a new action yet)
PostsController.rb
def new
@post = Post.new
end
Post.new creates an instance of the Post model and assigns that instance to @post.
new.slim
.container
h1.mt-4 New registration
= form_with url: posts_path, model: @post do |form|
.form-group
= form.label :title, 'title'
= form.text_field :title, class: 'form-control'
.form-group
= form.label :content, 'Contents'
= form.text_area :content, class: 'form-control'
= form.submit 'Registration', class: 'btn btn-primary'
Click here for form_with => How to use form_with
To briefly explain = You can set the transition destination when you press submit with form_with posts_path.
I would like to see the routing of the create action here.
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
If nothing is described in Prefix, it will be the Prefix that exists at the beginning by following the above.
In this case, Prefix is posts.
Therefore, there are two types of posts_path, posts # index and posts # create.
Verb (** HTTP method **) is how to judge these two.
For form_with, the default HTTP method is set to POST.
This means that ** posts_path ** written in new.slim means execute the create action of PostsController
.
First, check what kind of parameters are sent.
PostsController.rb
def create
binding.pry
end
Let's see in the console how the data is sent by writing.
9: def create
=> 10: binding.pry
11: end
[1] pry(#<PostsController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"d9j/87bg84JqMg5cPr7HwMKi8PIbw8gmEhMj4EvgIblmRKqdmGAdmk1THcW9095M2cBdQxGigM/PZ+VYl9ZGYA==", "post"=>{"title"=>"Title 2", "content"=>"Content 2"}, "commit"=>"Registration", "controller"=>"posts", "action"=>"create"} permitted: false>
[2] pry(#<PostsController>)> params[:post][:title]
=> "Title 1"
[3] pry(#<PostsController>)> params[:post][:content]
=> "Content 2"
By writing like this, I was able to confirm the contents. Then save this.
PostsController.rb
def create
@post = Post.new(title: params[:post][:title], content: params[:post][:content])
@post.save
end
Then you can save it. However, this time, we will save it using a mechanism called strong parameters that prevent invalid parameters.
PostsController.rb
def create
@post = Post.new
@post.save(post_params)
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :content)
end
The modified code looks like this. This time, the conditional branch code is not described in the if statement because the title and content can be saved even if they are empty.
The last description of the create action redirect_to posts_path
causes the url to transition to/
, that is, to execute the index action of PostsController.
Now that the data has been generated by the create action, the next step is to acquire and display the data.
PostsController.rb
def index
@posts = Post.all
end
You can get all the data of the model with model .all and assign the obtained data to @posts. @posts is plural because it is expected to contain multiple data. By the way, the data is included in the following form.
[#<Post:0x00007f94bc4d83b8 id: 1, title: "Title 1", content: "Content 1", created_at: Sat, 15 Aug 2020 05:19:09 UTC +00:00, updated_at: Sat, 15 Aug 2020 05:19:09 UTC +00:00>,
#<Post:0x00007f94bc4ae838 id: 2, title: "Title 2", content: "Content 2", created_at: Sat, 15 Aug 2020 05:19:22 UTC +00:00, updated_at: Sat, 15 Aug 2020 05:19:22 UTC +00:00>]
Let's display these.
index.slim
.container
h1.mt-4 Posts#index
= link_to 'sign up', new_post_path
//Postscript
- @posts.each do |post|
.card.my-4
.card-header
= post.title
.card-body
p.card-text
= post.content
@posts is defined in the index action. It is fetched one by one with each method, and one fetched data is put in the post. = post.title, = post.content refers to the title column and content column of post data. The screen will look like the one below.
First, set up a button to move to the details screen under each data.
index.slim
.container
h1.mt-4 Posts#index
= link_to 'sign up', new_post_path
- @posts.each do |post|
.card.my-4
.card-header
= post.title
.card-body
p.card-text
= post.content
//Postscript
= link_to 'Details', post_path(post), class: 'btn btn-success mr-2'
Notice that it takes a post as the ** argument of post_path **. Let's take a look at the routing of the show action.
Prefix Verb URI Pattern Controller#Action
post GET /posts/:id(.:format) posts#show
The URI Pattern has a: id and this value changes dynamically. (Example: / posts / 1
, / posts / 7
)
If only post_path is used, the id is unknown, so take an argument. (This time post)
By doing this, you can get the id of the post and you can transition to / posts /: id
.
Now let's see what parameters are sent to the show action.
PostsController.rb
def show
binding.pry
end
Stop at binding.pry and check the console
pry(#<PostsController>)> params
=> <ActionController::Parameters {"controller"=>"posts", "action"=>"show", "id"=>"1"} permitted: false>
[2] pry(#<PostsController>)> params[:id]
=> "1"
You can get the id by writing params [: id]
like this.
Use this id to get one data from the Post model data.
PostsController.rb
def show
@post = Post.find(params[:id])
end
By setting it as model .find (id number), one data that matches the id and id number of the model is acquired. Next is show.slim.
show.slim
.container
.card.my-4
.card-header
= @post.title
.card-body
p.card-text
= @post.content
A screen like this will be displayed when the transition occurs.
First, set up the edit button in the same way as the detail button.
index.slim
.container
h1.mt-4 Posts#index
= link_to 'sign up', new_post_path
- @posts.each do |post|
.card.my-4
.card-header
= post.title
.card-body
p.card-text
= post.content
= link_to 'Details', post_path(post), class: 'btn btn-success mr-2'
//Postscript
= link_to 'Edit', edit_post_path(post), class: 'btn btn-primary mr-2'
Let's take a look at the routing of the edit action.
Prefix Verb URI Pattern Controller#Action
edit_post GET /posts/:id/edit(.:format) posts#edit
Then define the edit action.
PostsController.rb
def edit
@post = Post.find(params[:id])
end
The description is the same as the show action. Then write edit.slim.
edit.slim
.container
h1.mt-4 Edit
= form_with url: post_path(@post), model: @post do |form|
.form-group
= form.label :title, 'title'
= form.text_field :title, class: 'form-control'
.form-group
= form.label :content, 'Contents'
= form.text_area :content, class: 'form-control'
= form.submit 'update', class: 'btn btn-primary'
The content is almost the same as new.slim, so the explanation is omitted. post_path (@post) is the same as when defined in new.slim, and this HTTP method is PATCH. Let's take a look at the routing.
Prefix Verb URI Pattern Controller#Action
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
Since the HTTP method is PATCH, the action to be executed when the update button is pressed this time is the PostsController update action
.
Define the update action.
PostsController.rb
def update
@post = Post.find(params[:id])
@post.update(post_params)
redirect_to posts_path
end
This is almost the same as the create action. The difference from the create action is whether the model is new or find.
Finally delete the data. Set up a delete button as well as an edit button. Let's take a look at the routing of the destroy action.
Prefix Verb URI Pattern Controller#Action
post GET /posts/:id(.:format) posts#show
DELETE /posts/:id(.:format) posts#destroy
Since they are the same post_path, they are distinguished by the HTTP method. By setting method :: HTTP method
, the action of the specified HTTP method in the specified path will be executed. I will actually describe it.
index.slim
.container
h1.mt-4 Posts#index
= link_to 'sign up', new_post_path
- @posts.each do |post|
.card.my-4
.card-header
= post.title
.card-body
p.card-text
= post.content
= link_to 'Details', post_path(post), class: 'btn btn-success mr-2'
= link_to 'Edit', edit_post_path(post), class: 'btn btn-primary mr-2'
//Postscript
= link_to 'Delete', post_path(post), method: :delete, class: 'btn btn-danger'
Then define the destroy action.
PostsController.rb
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path
end
You have now implemented CRUD for one model.
For the time being, the completed code is listed below.
Postscontroller.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
@post.save
redirect_to posts_path
end
def show
@post = Post.find(params[:id])
end
def edit
@post = Post.find(params[:id])
end
def update
@post = Post.find(params[:id])
@post.update(post_params)
redirect_to posts_path
end
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :content)
end
end
For the time being, I was able to write all the content I wanted to show to myself a year ago.
Recommended Posts