- generate model and controller in one command
bin/rails g resource support_request name email message:text department done:boolean
- quickly reset data
bin/rake db:drop db:create db:migrate db:seed
- find will throw an exception if not found
- find_by_id returns nil if not found
- one to one
- one to many
- many to many
- Foreign key
- indexing
- Defines database table / model structures
- use the following command to create a migration file when try to add in associations
bin/rails g migration AddProjectToTasks project:references
- the migration will look like this
add_reference :tasks, :project, index: true, foreign_key: true
# # the PATH for this URL (index action) is the same as the PATH for the
# # create action (with POST) so we can just use the one we defined for the
# # create which is `questions_path`
# get "/questions" => "questions#index"
resources :questions do
get :search, on: :collection
patch :mark_done, on: :member
post :approve
# By defining `resources :answers` nested inside `resources :questions`
# Rails will defined all the answers routes prepended with
# `/questions/:question_id`. This enables us to have the question_id handy
# so we can create the answer associated with a question with `question_id`
resources :answers, only:[:create, :destroy]
end
resources :answers
+----+-------+-------------+------------+------------+------------+
| id | body | question_id | created_at | updated_at | xx.. |
+----+-------+-------------+------------+------------+------------+
| | | | | | |
+----+-------+-------------+------------+------------+------------+
-
question_id: foreign key constraint, index
-
create rails models with association
> bin/rails g model answer title:text question:references
- migration will contain the following code
class CreateAnswers < ActiveRecord::Migration
def change
create_table :answers do |t|
t.text :body
# t.references :question wil generate an integer fild that is called
# question_id (rails convention)
# index: true willatomatically create an index on the 'question_id'
# field
# foreign_key: true will automatically create a foreign key constraint
# for the 'question_id' field
t.references :question, index: true, foreign_key: true
t.timestamps null: false
end
end
end
- execute the migrate
bin/rake db:migrate
- the database will generate a constraint and an index for the foreign key: question_id
- the answer model now looks like this:
class Answer < ActiveRecord::Base
belongs_to :question
end
- question model:
class Question < ActiveRecord::Base
# This will establish a `has_many` association with answers. This assumes
# that your answer model has a `question_id` integer field that references
# the question. with has_many `answers` must be plural (Rails convention).
# We must pass a `dependent` option to maintain data integrity. The possible
# values you can give it are: :destroy or :nullify
# With :destroy: if you delete a question it will delete all associated answers
# With :nullify: if you delete a question it will update the `question_id` NULL
# => for all associated records (they won't get deleted)
has_many :answers, dependent: :destroy
...
- now we can create records and specify the association like this:
Answer.create(body: "Hey there!", question: q)
- dependent: :destroy will allow the child record to be deleted before parent record is about to be deleted
2.2.3 :004 > q = Question.last
Question Load (0.9ms) SELECT "questions".* FROM "questions" ORDER BY "questions"."id" DESC LIMIT 1
+-----+-------------+-------------+-------------+--------------+------------+
| id | title | body | created_at | updated_at | view_count |
+-----+-------------+-------------+-------------+--------------+------------+
| 207 | Use inpu... | this bod... | 2016-02-... | 2016-02-0... | 0 |
+-----+-------------+-------------+-------------+--------------+------------+
1 row in set
2.2.3 :005 > Answer.create(body: "Hey there!", question: q)
(0.1ms) BEGIN
SQL (0.6ms) INSERT INTO "answers" ("body", "question_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["body", "Hey there!"], ["question_id", 207], ["created_at", "2016-02-10 20:03:33.520373"], ["updated_at", "2016-02-10 20:03:33.520373"]]
(1.2ms) COMMIT
+----+------------+-------------+---------------------+---------------------+
| id | body | question_id | created_at | updated_at |
+----+------------+-------------+---------------------+---------------------+
| 4 | Hey there! | 207 | 2016-02-10 20:03... | 2016-02-10 20:03... |
+----+------------+-------------+---------------------+---------------------+
1 row in set
2.2.3 :006 > q.destroy
(0.2ms) BEGIN
Answer Load (0.2ms) SELECT "answers".* FROM "answers" WHERE "answers"."question_id" = $1 [["question_id", 207]]
SQL (0.1ms) DELETE FROM "answers" WHERE "answers"."id" = $1 [["id", 4]]
SQL (0.3ms) DELETE FROM "questions" WHERE "questions"."id" = $1 [["id", 207]]
(6.3ms) COMMIT
+-----+-------------+-------------+-------------+--------------+------------+
| id | title | body | created_at | updated_at | view_count |
+-----+-------------+-------------+-------------+--------------+------------+
| 207 | Use inpu... | this bod... | 2016-02-... | 2016-02-0... | 0 |
+-----+-------------+-------------+-------------+--------------+------------+
1 row in set
- if have nullify instead of destroy for the association constraint:
has_many :answers, dependent: :nullify
- when deleting the parent record:
2.2.3 :001 > a = Answer.new(body: "abc")
+----+------+-------------+------------+------------+
| id | body | question_id | created_at | updated_at |
+----+------+-------------+------------+------------+
| | abc | | | |
+----+------+-------------+------------+------------+
1 row in set
2.2.3 :002 > a.question = Question.last
Question Load (0.6ms) SELECT "questions".* FROM "questions" ORDER BY "questions"."id" DESC LIMIT 1
+-----+-------------+-------------+-------------+--------------+------------+
| id | title | body | created_at | updated_at | view_count |
+-----+-------------+-------------+-------------+--------------+------------+
| 208 | Amazing ... | amazing ... | 2016-02-... | 2016-02-0... | 3 |
+-----+-------------+-------------+-------------+--------------+------------+
1 row in set
2.2.3 :003 > a.save
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "answers" ("body", "question_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["body", "abc"], ["question_id", 208], ["created_at", "2016-02-10 19:55:57.242718"], ["updated_at", "2016-02-10 19:55:57.242718"]]
(1.3ms) COMMIT
true
2.2.3 :004 > Question.last.destroy
- the child will be updated before the parent is destroyed
Question Load (0.5ms) SELECT "questions".* FROM "questions" ORDER BY "questions"."id" DESC LIMIT 1
(0.1ms) BEGIN
SQL (0.2ms) UPDATE "answers" SET "question_id" = NULL WHERE "answers"."question_id" = $1 [["question_id", 208]]
SQL (0.3ms) DELETE FROM "questions" WHERE "questions"."id" = $1 [["id", 208]]
(6.3ms) COMMIT
+-----+-------------+-------------+-------------+--------------+------------+
| id | title | body | created_at | updated_at | view_count |
+-----+-------------+-------------+-------------+--------------+------------+
| 208 | Amazing ... | amazing ... | 2016-02-... | 2016-02-0... | 3 |
+-----+-------------+-------------+-------------+--------------+------------+
1 row in set
- create an answer controller
bin/rails g controller answers
- controller
class AnswersController < ApplicationController
def create
@question = Question.find params[:question_id]
answer_params = params.require(:answer).permit(:body)
@answer = Answer.new answer_params
@answer.question = @question
if @answer.save
redirect_to question_path(@question), notice: "Answer created!"
else
render "/questions/show"
end
# render json: params
end
end
- in question's show view, add in an answer form
<%= form_for [@question, @answer] do |f| %>
<div>
<%= f.label :body %>
<%= f.text_area :body %>
</div>
<%= f.submit %>
<% end %>
- model
class Answer < ActiveRecord::Base
belongs_to :question
# unique answer per question
validates :body, presence: true, uniqueness:{scope: :question_id}
end
- view
<% @question.answers.each do |ans| %>
<%= ans.body %>
<%= link_to "Delete", question_answer_path(@question, ans),
method: :delete,
data: {confirm: "Are you sure?"} %>
<hr>
<% end %>
- controller
def destroy
# question = Question.find params[:question_id]
# answer = question.find params[:id]
answer = Answer.find params[:id]
answer.destroy
redirect_to question_path(params[:question_id]), notice: "Answer deleted!"
end
- add category to question
- question model:
belongs_to :category
...
def category_name
category.name if category
end
...
- view
<%= f.collection_select(:category_id, Category.all, :id, :name, prompt: true) %>
- controller
question_params = params.require(:question).permit([:title, :body, :category_id])
- have comments for each answer which blongs to questions
# We do this to avoid triple nesting comments under `resources :answers` within
# the `resources :questions` as it will be very cumbersome to generate routes
# and use forms. We don't need another set of `answers` routes in here so
# pass the `only: []` option to it.
resources :answers, only: [] do
resources :comments, only: [:create, :destroy]
end
# This enables us to access all comments created for all the question's answers.
# This generated a single SQL statement with 'INNER JOIN' to accomplish it
has_many :comments, through: :answers