Ruby on Rails 애플리케이션을 위한 중첩 리소스 만들기

소개

Ruby on Rails는 개발자에게 응용 프로그램 개발에 대한 의견이 있는 접근 방식을 제공하는 Ruby로 작성된 웹 애플리케이션 프레임워크입니다. Rails를 사용하면 개발자들은 다음을 얻게 됩니다:

  • 라우팅, 상태 데이터 및 자산 관리와 같은 것들을 다루는 규칙.
  • A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.

Rails 애플리케이션에 복잡성을 추가할수록, 여러 모델을 사용하게 될 것입니다. 이 모델들은 응용 프로그램의 비즈니스 로직을 나타내며 데이터베이스와 인터페이스합니다. 관련된 모델을 추가하는 것은 그들 간의 의미 있는 관계를 설정하는 것을 의미하며, 이는 그 후 응용 프로그램의 컨트롤러를 통해 정보가 어떻게 전달되고, 사용자에게 어떻게 제시되는지에 영향을 미칩니다.

이 튜토리얼에서는 상어에 관한 사실을 제공하는 기존의 Rails 애플리케이션을 기반으로 작업할 것입니다. 이 응용 프로그램에는 이미 상어 데이터를 처리하는 모델이 있지만, 개별 상어에 대한 포스트를 위한 중첩된 리소스를 추가할 것입니다. 이를 통해 사용자들은 개별 상어에 대한 더 넓은 생각과 의견을 개발할 수 있습니다.

전제 조건

이 튜토리얼을 따르려면 다음이 필요합니다:

  • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
  • 로컬 머신 또는 개발 서버에 Node.jsnpm이 설치되어 있어야 합니다. 이 튜토리얼에서는 Node.js 버전 10.16.3과 npm 버전 6.9.0을 사용합니다. Ubuntu 18.04에 Node.js 및 npm을 설치하는 지침은 “Ubuntu 18.04에 Node.js 설치하기“의 “PPA를 사용하여 설치” 섹션의 지침을 따르십시오.
  • 로컬 머신 또는 개발 서버에 Ruby, rbenv, 그리고 Rails가 설치되어 있어야 합니다. “Ubuntu 18.04에 rbenv로 Ruby on Rails 설치하기“의 단계 1-4를 따르십시오. 이 튜토리얼에서는 Ruby 2.5.1, rbenv 1.1.2, 그리고 Rails 5.2.3을 사용합니다.
  • SQLite가 설치되어 있어야 하며, 기본 상어 정보 애플리케이션이 생성되어 있어야 합니다. “Ruby on Rails 애플리케이션 만들기“의 지시에 따릅니다.

단계 1 — 중첩 모델 스캐폴딩

우리의 애플리케이션은 Active Record 연결을 활용하여 SharkPost 모델 간의 관계를 구축할 것입니다: 게시물은 특정 상어에 속하며, 각 상어는 여러 게시물을 가질 수 있습니다. 따라서 우리의 SharkPost 모델은 belongs_tohas_many 관계를 통해 관련될 것입니다.

이 방식으로 애플리케이션을 구축하기 위한 첫 번째 단계는 Post 모델과 관련된 리소스를 생성하는 것입니다. 이를 위해 우리는 rails generate scaffold 명령을 사용할 수 있습니다. 이 명령은 우리에게 모델, 데이터베이스 스키마를 변경하는 데이터베이스 마이그레이션, 컨트롤러, 표준 생성, 읽기, 수정 및 삭제 (CRUD) 작업을 관리하는 전체 뷰 세트 및 부분, 도우미 및 테스트 템플릿을 제공합니다. 이러한 리소스를 수정해야 하지만, scaffold 명령을 사용하면 시작점으로 사용할 수 있는 구조를 생성해 줍니다.

먼저, 사전 요구 사항에서 생성한 Rails 프로젝트의 sharkapp 디렉토리에 있는지 확인하십시오:

  1. cd sharkapp

다음 명령을 사용하여 Post 리소스를 생성하십시오:

rails generate scaffold Post body:text shark:references

body:text를 사용하여 Rails에게 posts 데이터베이스 테이블에 body 필드를 포함하도록 지시합니다. 이 테이블은 Post 모델에 매핑되는 테이블입니다. 또한 :references 키워드를 포함하여 SharkPost 모델 간의 연관성을 설정합니다. 구체적으로, 이는 sharks 데이터베이스의 각 상어 항목을 나타내는 외래 키가 posts 데이터베이스에 추가되도록 보장합니다.

명령을 실행한 후에는 Rails가 애플리케이션을 위해 생성한 리소스를 확인할 수 있습니다. 계속하기 전에 데이터베이스 마이그레이션 파일을 확인하여 이제 모델과 데이터베이스 테이블 간에 존재하는 관계를 살펴볼 수 있습니다. 다음 명령을 사용하여 파일 내용을 확인하고 여기에 표시된 타임스탬프를 자신의 마이그레이션 파일에 대체하십시오:

  1. cat db/migrate/20190805132506_create_posts.rb

다음 출력이 표시됩니다:

Output
class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

테이블에 상어 외래 키 열이 포함되어 있는 것을 볼 수 있습니다. 이 키는 model_name_id 형식을 취할 것입니다. 여기서는 shark_id입니다.

Rails는 모델 간의 관계를 다른 곳에서도 설정했습니다. 다음 명령을 사용하여 새로 생성된 Post 모델을 살펴보십시오:

cat app/models/post.rb
Output
class Post < ApplicationRecord belongs_to :shark end

belongs_to 연관은 선언 모델의 단일 인스턴스가 지정된 모델의 단일 인스턴스에 속하는 관계를 설정합니다. 우리 애플리케이션의 경우, 이는 단일 게시물이 단일 상어에 속한다는 것을 의미합니다.

이 관계를 설정하는 것 외에도 rails generate scaffold 명령은 우리가 루비 온 레일 애플리케이션 구축 방법3단계에서 상어 리소스에 대해 한 것처럼 게시물에 대한 라우트와 뷰를 생성했습니다.

이것은 유용한 시작이지만 우리 모델과 라우트 간의 관계가 원하는 대로 작동하려면 몇 가지 추가적인 라우팅을 구성하고 Shark 모델의 Active Record 연관성을 확립해야 할 것입니다.

단계 2 — 부모 모델에 대한 중첩된 라우트 및 연관성 지정

레일즈는 이미 rails generate scaffold 명령의 :references 키워드 덕분에 우리의 Post 모델에 belongs_to 연관성을 설정했지만, 이 관계가 올바르게 작동하려면 우리의 Shark 모델에서도 has_many 연관성을 지정해야 합니다. 또한 레일즈가 우리에게 제공한 기본 라우팅을 변경하여 게시물 리소스를 상어 리소스의 자식으로 만들어야 합니다.

has_many 관계를 Shark 모델에 추가하려면 nano 또는 선호하는 편집기를 사용하여 app/models/shark.rb 파일을 엽니다:

  1. nano app/models/shark.rb

다음 줄을 파일에 추가하여 상어와 게시물 간의 관계를 설정합니다:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

여기서 고려해야 할 중요한 점은 특정 상어가 삭제된 후 게시물이 어떻게 처리되는지입니다. 우리는 아마도 삭제된 상어와 관련된 게시물이 데이터베이스에 계속 유지되기를 원하지 않을 것입니다. 주어진 상어와 관련된 게시물이 삭제된 경우 해당 상어가 삭제될 때 해당 상어와 관련된 게시물이 삭제되도록 하려면 관련성과 함께 dependent 옵션을 포함할 수 있습니다.

destroy 액션에서 특정 상어를 삭제하면 관련된 게시물이 삭제되도록하는 코드를 파일에 추가합니다:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts , dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

이러한 변경을 완료하면 파일을 저장하고 닫습니다. nano을 사용하는 경우 CTRL+X를 누른 다음 Y를 누르고 ENTER를 누르면 됩니다.

다음으로, 리소스 라우트 간의 관계를 수정하려면 config/routes.rb 파일을 엽니다:

  1. nano config/routes.rb

현재 파일은 다음과 같습니다:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :posts 
  resources :sharks

  root 'sharks#index'
  # 이 파일에서 사용 가능한 DSL에 대한 자세한 내용은 http://guides.rubyonrails.org/routing.html을 참조하십시오
end

현재 코드는 라우트 간에 독립적인 관계를 설정하는데, 우리가 표현하고자 하는 것은 상어와 관련된 게시물 간의 종속적인 관계입니다. 종속적인 관계

라우트 선언을 업데이트하여 :sharks:posts의 상위로 만듭니다. 파일의 코드를 다음과 같이 업데이트하십시오:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks do
    resources :posts
end
  root 'sharks#index'
  # 이 파일에서 사용 가능한 DSL에 대한 자세한 내용은 http://guides.rubyonrails.org/routing.html을(를) 참조하십시오.
end

편집을 완료한 후 파일을 저장하고 닫으십시오.

이러한 변경 사항이 적용되면 posts 컨트롤러를 업데이트할 차례입니다.

단계 3 — 포스트 컨트롤러 업데이트

모델 간의 연관은 특정 상어와 연결된 새로운 포스트 인스턴스를 생성하는 데 사용할 수 있는 메서드를 제공합니다. 이러한 메서드를 사용하려면 포스트 컨트롤러에 추가해야 합니다.

포스트 컨트롤러 파일을 열어보십시오:

  1. nano app/controllers/posts_controller.rb

현재 파일은 다음과 같습니다:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # GET /posts
  # GET /posts.json
  def index
    @posts = Post.all
  end

  # GET /posts/1
  # GET /posts/1.json
  def show
  end

  # GET /posts/new
  def new
    @post = Post.new
  end

  # GET /posts/1/edit
  def edit
  end

  # POST /posts
  # POST /posts.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /posts/1
  # DELETE /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = Post.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

우리의 상어 컨트롤러와 마찬가지로, 이 컨트롤러의 메서드는 관련된 Post 클래스의 인스턴스와 함께 작동합니다. 예를 들어, new 메서드는 Post 클래스의 새 인스턴스를 생성하고, index 메서드는 클래스의 모든 인스턴스를 가져오며, set_post 메서드는 findparams를 사용하여 특정 게시물을 id로 선택합니다. 그러나 특정 상어 인스턴스와 연관된 게시물 인스턴스를 사용하려면 현재 Post 클래스가 독립적인 엔티티로 작동하고 있으므로 이 코드를 수정해야 합니다.

우리의 수정은 두 가지를 활용할 것입니다:

  • 우리가 모델에 belongs_tohas_many 연관을 추가했을 때 우리에게 가능해진 방법들. 구체적으로, 우리는 이제 Shark 모델에서 정의한 has_many 연관 덕분에 build 메소드에 접근할 수 있습니다. 이 메소드를 통해 우리는 posts 데이터베이스에 있는 shark_id 외래 키를 사용하여 특정 상어 객체와 관련된 포스트 객체 컬렉션을 생성할 수 있습니다.
  • 우리가 중첩된 posts 경로를 생성했을 때 가능해진 라우트와 라우팅 헬퍼들. 리소스간 중첩된 관계를 생성할 때 가능한 예제 라우트의 전체 목록은 Rails 문서를 참조하십시오. 현재로서는 각 특정 상어에 대해 — 예를 들어 sharks/1 — 해당 상어와 관련된 포스트를 위한 연관된 라우트가 있을 것입니다: sharks/1/posts. 이러한 중첩된 경로를 참조하는 라우팅 헬퍼들인 shark_posts_path(@shark)edit_sharks_posts_path(@shark)도 있을 것입니다.

파일에서, 우리는 컨트롤러의 각 액션 전에 실행될 get_shark 메소드를 작성할 것입니다. 이 메소드는 shark_id로 상어 인스턴스를 찾아 로컬 @shark 인스턴스 변수를 생성할 것입니다. 이 변수를 파일에서 사용할 수 있으면 다른 메소드에서 특정 상어에 대한 포스트와 관련시키는 것이 가능해질 것입니다.

파일 아래에 다음 메소드를 추가하십시오:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
  def get_shark
@shark = Shark.find(params[:shark_id])
end
  # 콜백을 사용하여 작업 또는 제약 사항을 액션 간에 공유합니다.
. . . 

다음으로, 기존 필터 앞에 파일의 상단에 해당 필터를 추가하십시오:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark

이렇게하면 파일에 정의된 각 액션 앞에서 get_shark가 실행됩니다.

다음으로, 이 @shark 인스턴스를 사용하여 index 메소드를 다시 작성할 수 있습니다. Post 클래스의 모든 인스턴스를 가져 오는 대신이 메소드를 사용하여 특정 상어 인스턴스와 연관된 모든 게시물 인스턴스를 반환하려고합니다.

다음과 같이 index 메소드를 수정하십시오:

~/sharkapp/controllers/posts_controller.rb
. . .
  def index
    @posts = @shark.posts
  end
. . .

new 메소드도 특정 상어와 연관된 새 게시물 인스턴스를 원하므로 이와 유사한 수정이 필요합니다. 이를 위해 build 메소드를 사용하고 로컬 @shark 인스턴스 변수를 사용할 수 있습니다.

new 메소드를 다음과 같이 변경하십시오:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def new
    @post = @shark.posts.build
  end
. . . 

이 메소드는 get_shark 메소드에서 지정된 특정 상어 인스턴스와 연관된 게시물 객체를 생성합니다.

다음으로, new에 가장 밀접하게 관련된 메소드인 create를 다룰 것입니다. create 메소드는 두 가지 작업을 수행합니다. 사용자가 new 양식에 입력 한 매개 변수를 사용하여 새 게시물 인스턴스를 빌드하고 오류가 없는 경우 해당 인스턴스를 저장하고 루트 도우미를 사용하여 사용자가 새 게시물을 볼 수있는 곳으로 리디렉션합니다. 오류가있는 경우 new 템플릿을 다시 렌더링합니다.

create 메소드를 다음과 같이 업데이트하세요:

~/sharkapp/controllers/posts_controller.rb
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

다음으로 update 메소드를 살펴보겠습니다. 이 메소드는 메소드 자체에서 명시적으로 설정되지 않은 @post 인스턴스 변수를 사용합니다. 이 변수는 어디서 온 것입니까?

파일 상단의 필터를 살펴보세요. 두 번째로 자동 생성된 before_action 필터가 답을 제공합니다:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  . . .

update 메소드(show, edit, destroy와 같이)는 set_post 메소드에서 @post 변수를 가져옵니다. 이 메소드는 다음과 같이 다른 private 메소드와 함께 나열된 get_shark 메소드 아래에 있습니다:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = Post.find(params[:id])
  end
. . .

파일의 다른 곳에서 사용한 메소드와 일치하도록 이 메소드를 수정해야 합니다. 이 메소드는 우리 모델 간의 연관 관계와 그 연관 관계에 따라 제공되는 메소드(build와 같은) 덕분에, 각 포스트 인스턴스가 특정 상어와 연관된 객체 컬렉션의 일부임을 고려할 때 논리적입니다. 따라서 특정 포스트를 쿼리할 때는 특정 상어와 관련된 포스트 컬렉션을 쿼리해야 합니다.

set_post를 다음과 같이 업데이트하세요:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = @shark.posts.find(params[:id])
  end
. . .

전체 Post 클래스의 특정 인스턴스를 id로 찾는 대신, 특정 상어와 관련된 포스트 컬렉션에서 일치하는 id를 찾습니다.

그 방법을 업데이트하면 updatedestroy 메소드를 살펴볼 수 있습니다.

update 메소드는 set_post에서 @post 인스턴스 변수를 사용하고 사용자가 edit 폼에 입력한 post_params와 함께 사용합니다. 성공한 경우, 우리는 Rails가 사용자를 특정 상어와 연관된 게시물의 index 뷰로 다시 보내도록 하고 싶습니다. 오류가 발생한 경우, Rails는 edit 템플릿을 다시 렌더링합니다.

이 경우, 우리가 필요로 하는 유일한 변경 사항은 성공적인 업데이트를 처리하기 위해 redirect_to 문에 있습니다. shark_post_path(@shark)로 리디렉션하도록 업데이트하십시오. 이는 선택한 상어의 게시물의 index 뷰로 리디렉션합니다:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
. . .

다음으로, destroy 메소드에 유사한 변경을 적용할 것입니다. 성공한 경우 redirect_to 메소드를 shark_posts_path(@shark)로 요청을 리디렉션하도록 업데이트하십시오:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def destroy
    @post.destroy
     respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
. . .

이것이 우리가 할 마지막 변경입니다. 이제 다음과 같은 게시물 컨트롤러 파일이 있습니다:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # GET /posts
  # GET /posts.json
  def index
    @posts = @shark.posts
  end

  # GET /posts/1
  # GET /posts/1.json
  def show
  end

  # GET /posts/new
  def new
    @post = @shark.posts.build
  end

  # GET /posts/1/edit
  def edit
  end

  # POST /posts
  # POST /posts.json
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /posts/1
  # DELETE /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

   def get_shark
     @shark = Shark.find(params[:shark_id])
   end
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

컨트롤러는 뷰 템플릿에서 데이터베이스로 및 그 반대로 정보가 전달되는 방식을 관리합니다. 우리의 컨트롤러는 이제 게시물이 특정 상어와 연관되어 있는 SharkPost 모델 간의 관계를 반영합니다. 이제 사용자가 특정 상어에 대한 게시물 정보를 입력하고 수정하는 뷰 템플릿을 수정할 수 있습니다.

단계 4 — 뷰 수정

우리의 뷰 템플릿 수정은 게시물과 관련된 템플릿을 변경하고, 특정 상어와 관련된 게시물을 사용자가 볼 수 있도록 상어 show 뷰를 수정하는 것을 포함합니다.

먼저 게시물에 대한 기본 템플릿인 form 부분을 시작합시다. 이 폼을 열어주세요:

  1. nano app/views/posts/_form.html.erb

대신에 post 모델만 form_with 폼 헬퍼에 전달하는 대신 sharkpost 모델을 모두 전달하되, post를 자식 리소스로 설정합니다.

파일의 첫 줄을 다음과 같이 변경하여 우리의 상어와 포스트 리소스 간의 관계를 반영합니다:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . . 

다음으로, 뷰에서 핵심 정보가 아닌 shark_id를 나열하는 부분을 삭제합니다.

첫 줄을 편집한 결과와 shark_id 섹션을 삭제한 완성된 폼은 다음과 같이 보입니다:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

편집을 마쳤으면 파일을 저장하고 닫으십시오.

다음으로 특정 상어와 관련된 포스트를 보여줄 index 뷰를 엽니다:

  1. nano app/views/posts/index.html.erb

rails generate scaffold 명령을 통해 Rails가 템플릿의 대부분을 생성했으므로, 각 포스트의 body 필드와 해당하는 shark를 보여주는 테이블이 완성되었습니다.

그러나 이미 수정한 다른 코드와 마찬가지로 이 템플릿은 포스트를 독립적인 엔티티로 처리합니다. 우리는 모델 간의 관계와 이러한 관계가 제공하는 컬렉션과 헬퍼 메서드를 활용하려고 합니다.

테이블 본문에서 다음을 업데이트합니다:

먼저, post.sharkpost.shark.name으로 업데이트하여 테이블이 상어 객체 자체의 식별 정보 대신에 연관된 상어의 이름 필드를 포함하도록 합니다:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
. . . 

다음으로, 사용자가 원래 상어로 돌아갈 수 있는 방법을 원할 것이므로 Show 리디렉션을 연결된 상어에 대한 show 뷰로 변경합니다. 우리는 여기서 컨트롤러에서 설정한 @shark 인스턴스 변수를 활용할 수 있습니다. Rails는 컨트롤러에서 생성된 인스턴스 변수를 모든 뷰에서 사용할 수 있게 합니다. 또한 링크 텍스트를 Show에서 Show Shark로 변경하여 사용자가 그 기능을 더 잘 이해할 수 있도록 합니다.

이 줄을 다음과 같이 업데이트합니다:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>

다음 줄에서는 사용자가 게시물을 편집할 때 올바른 중첩된 경로로 이동되도록합니다. 이것은 사용자가 posts/post_id/edit로 이동하는 대신 sharks/shark_id/posts/post_id/edit로 이동되어야 함을 의미합니다. 이를 위해 shark_post_path 라우팅 헬퍼와 모델을 사용하고, Rails는 이를 URL로 처리합니다. 또한 링크 텍스트를 업데이트하여 기능을 더 명확하게 만듭니다.

Edit 줄을 다음과 같이 업데이트합니다:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>

다음으로, Destroy 링크에 유사한 변경을 추가하여 문자열의 기능을 업데이트하고 sharkpost 리소스를 추가합니다:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>

마지막으로, 폼 하단에 있는 New Post 경로를 업데이트하여 사용자가 새로운 게시물을 만들려고 할 때 적절한 중첩된 경로로 이동하도록합니다. 파일의 마지막 줄을 new_shark_post_path(@shark) 라우팅 헬퍼를 사용하도록 업데이트합니다:

~/sharkapp/app/views/posts/index.html.erb
. . . 
<%= link_to 'New Post', new_shark_post_path(@shark) %>

완성된 파일은 다음과 같습니다:

~/sharkapp/app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Shark</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Post', new_shark_post_path(@shark) %>

파일 편집을 마치면 저장하고 닫으십시오.

다른 뷰에 대한 편집은 이전에 편집한 form 부분을 사용하기 때문에 많지 않을 것입니다. 그러나 다른 포스트 템플릿에서는 form 부분의 변경 사항을 반영하도록 link_to 참조를 업데이트할 것입니다.

app/views/posts/new.html.erb 파일을 엽니다:

  1. nano app/views/posts/new.html.erb

파일 하단의 link_to 참조를 shark_posts_path(@shark) 헬퍼를 사용하도록 업데이트합니다:

~/sharkapp/app/views/posts/new.html.erb
. . . 
<%= link_to 'Back', shark_posts_path(@shark) %>

이 변경 사항을 적용한 후 파일을 저장하고 닫으십시오.

다음으로, edit 템플릿을 엽니다:

  1. nano app/views/posts/edit.html.erb

Back 경로에 추가로, 중첩된 리소스를 반영하도록 Show를 업데이트할 것입니다. 파일의 마지막 두 줄을 다음과 같이 변경합니다:

~/sharkapp/app/views/posts/edit.html.erb
. . . 
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

파일을 저장하고 닫으십시오.

다음으로, show 템플릿을 엽니다:

nano app/views/posts/show.html.erb

파일 하단의 EditBack 경로를 다음과 같이 수정합니다:

~/sharkapp/app/views/posts/edit.html.erb
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

편집을 마치면 파일을 저장하고 닫으십시오.

마지막 단계로, 상세 정보 페이지를 열어 해당 상어에 대한 포스트가 개별적으로 표시되도록 show 뷰를 업데이트해야 합니다. 지금 해당 파일을 엽니다:

  1. nano app/views/sharks/show.html.erb

여기서의 편집은 폼에 Posts 섹션을 추가하고 파일 하단에 Add Post 링크를 추가하는 것입니다.

아래는 주어진 상어에 대한 사실입니다. 이 상어와 관련된 포스트 컬렉션의 각 인스턴스를 반복하고, 각 포스트의 본문을 출력하는 새 섹션을 추가할 것입니다.

다음 코드를 폼의 Facts 섹션 아래에 추가하고 파일 아래쪽의 리디렉션 위에 위치시킵니다:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<% for post in @shark.posts %>
    <ul>
      <li><%= post.body %></li>
  </ul>
<% end %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . . 

그다음, 특정 상어에 대한 새로운 포스트를 추가할 수 있는 리디렉트를 추가합니다:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

편집을 마쳤으면 파일을 저장하고 닫으십시오.

이제 애플리케이션의 모델, 컨트롤러 및 뷰를 변경하여 포스트가 항상 특정 상어와 관련되도록 했습니다. 마지막 단계로, 데이터베이스에 저장되는 데이터의 일관성을 보장하기 위해 Post 모델에 일부 유효성 검사를 추가할 수 있습니다.

단계 5 — 유효성 검사 추가 및 애플리케이션 테스트

단계 5루비 온 레일 애플리케이션 만들기에서는 데이터베이스 sharks에 저장되는 데이터의 일관성과 일관성을 보장하기 위해 Shark 모델에 유효성 검사를 추가했습니다. 이제 posts 데이터베이스에도 유사한 조치를 취하여 보장합니다.

Post 모델이 정의된 파일을 엽니다:

  1. nano app/models/post.rb

여기서는 게시물이 비어 있지 않고 다른 사용자가 게시한 내용과 중복되지 않도록 보장하려고 합니다. 이를 위해 파일에 다음 줄을 추가하십시오:

~/sharkapp/app/models/post.rb
class Post < ApplicationRecord
  belongs_to :shark
  validates :body, presence: true, uniqueness: true
end

편집을 마치면 파일을 저장하고 닫으십시오.

마지막 변경 사항을 적용했으므로 마이그레이션을 실행하고 애플리케이션을 테스트할 준비가 되었습니다.

먼저 마이그레이션을 실행하십시오:

  1. rails db:migrate

다음으로 서버를 시작하십시오. 로컬에서 작업하는 경우 다음과 같이 실행할 수 있습니다:

  1. rails s

개발 서버에서 작업 중이라면 대신 다음 명령을 실행하십시오:

  1. rails s --binding=your_server_ip

애플리케이션 루트로 이동하십시오. http://localhost:3000 또는 http://your_server_ip:3000.

필수 레일 프로젝트 자습서에서는 대표적인 대구 상어 항목을 추가하고 편집하는 방법을 안내했습니다. 추가적인 상어를 추가하지 않은 경우, 애플리케이션 랜딩 페이지는 다음과 같이 보일 것입니다:

표시 옆에 Great White의 이름을 클릭하세요. 이렇게 하면 이 상어의 show 뷰로 이동합니다. 상어의 이름과 사실을 볼 수 있으며 내용이 없는 게시물 헤더가 있습니다. 이 양식의 이 부분을 채우기 위해 게시물을 추가해 보겠습니다.

게시물 추가를 클릭하여 게시물 헤더 아래로 이동합니다. 이렇게 하면 게시물 index 뷰로 이동하게 되며 여기서 새 게시물을 선택할 기회가 주어집니다:

Step 6에서 구현한 인증 메커니즘 덕분에 이 단계에서 만든 사용자 이름과 비밀번호로 인증을 요청할 수 있습니다. 새 세션을 생성했는지 여부에 따라서입니다.

새 게시물을 클릭하여 게시물 new 템플릿으로 이동합니다:

내용 필드에 “These sharks are scary!”라고 입력하세요.

게시물 생성을 클릭하세요. 그러면 이 상어에 속하는 모든 게시물의 index 뷰로 리디렉션됩니다:

게시물 자원이 작동하므로 이제 원하는 데이터만 데이터베이스에 저장되도록 데이터 유효성을 테스트할 수 있습니다.

index 뷰에서 새 게시물을 클릭하세요. 새 양식의 내용 필드에 다시 “These sharks are scary!”를 입력해 보세요:

게시물 만들기를 클릭하십시오. 다음 오류가 표시됩니다:

뒤로를 클릭하여 주요 게시물 페이지로 돌아갑니다.

다른 유효성을 테스트하려면 새 게시물을 다시 클릭하십시오. 게시물을 비워두고 게시물 만들기를 클릭하십시오. 다음 오류가 표시됩니다:

중첩된 리소스와 유효성이 올바르게 작동되므로 이제 추가 개발을 위한 출발점으로 사용할 수 있는 작동하는 Rails 애플리케이션이 있습니다.

결론

지금 Rails 애플리케이션이 준비되었으므로 스타일링 및 다른 프론트엔드 구성 요소 개발과 같은 작업을 할 수 있습니다. 라우팅 및 중첩된 리소스에 대해 더 알고 싶다면 Rails 문서를 확인하는 것이 좋습니다.

애플리케이션에 프론트엔드 프레임워크를 통합하는 방법에 대해 자세히 알아보려면 Ruby on Rails 프로젝트에 React 프론트엔드 설정하는 방법을 확인하십시오.

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application