fields_for로 자식 테이블의 데이터를 단번에 작성(테스트도 쓰고 있습니다) [Rails][Rspec]

12243 단어 RSpecRails

전제



한 가게의 데이터를 만들 때 그 가게에 대한 의견도 단번에 만들고 싶습니다. 전제가 되는 환경은 다음과 같습니다.
  • Rails 5.2.4.2
  • rspec-rails 4.0.1

  • 테이블 정보



    테이블의 구조는 이런 느낌입니다.

    상위 테이블(shops)




    shops


    id

    이름


    자식 테이블(comments)




    comments


    id

    shop_id

    콘텐츠


    모델



    부모 모델, 하위 모델의 model 파일은 각각 이와 같이 되어 있습니다.

    부모 모델(Shop)



    models/shop.rb
    class Shop < ApplicationRecord
      has_many :comments
      accepts_nested_attributes_for :comments
    end
    

    부모 모델에서 accepts_nested_attributes_for :comments를 지정하고 자식 모델의 데이터를 받아들이도록 하는 것이 포인트입니다.

    자식 모델(Comment)



    아이 모델의 기재에는 특별히 바뀐 것은 없습니다.

    models/comment.rb
    class Comment < ApplicationRecord
      belongs_to :shop
    end
    

    컨트롤러



    부모 모델 측의 컨트롤러에는 궁리해야 할 점이 몇 가지 있습니다.

    controllers/shops_controller.rb
    class ShopsController < ApplicationController
      def new
        @shop = Shop.new
        @shop.comments.build # ←一つ目のポイント
      end
    
      def create
        @shop = Shop.create(shop_params)
        if @shop.save
          # 成功したときの処理
        else
          # 成功しなかったときの処理
        end
      end
    
      private
    
      def shop_params # ↓2つ目のポイント
        params.require(:shop).permit(:name, comments_attributes: [:content])
      end
    
    end
    

    첫 번째 포인트는 new 액션에서 @shop.comments.build 라고 기재해, 아이 테이블인 comments 의 인스턴스를 build 까지 해 두는 것입니다. 이렇게 하면 상위 테이블에 묶는 comments를 만들 준비가 됩니다.

    두 번째 포인트는 shop_params 의 곳에서 comments_attributes: [:content] 라고 기재해, 아이 테이블의 요소도 permit 해 두는 것입니다. 요소가 둘 이상 있을 때는 comments_attributes: [:content, :attr2, :attr3] 와 같이 기재합니다.

    보기



    보기에는 다음과 같이 표시됩니다.

    views/shops/new.html.haml
    = form_with model: @shop, local: true do |f|
      = f.label :name, "店名"
      = f.text_field :name
    
      = f.fields_for :comments, local: true do |comments_form|
       = comments_form.label :content, "ショップへのコメント"
      = comments_form.text_field :content
    
    = f.submit "送信"
    

    이제 자식 테이블의 데이터도 단번에 만들어야합니다.
    여담입니다만, 이전 f.fields_for 곳을 fields_for 라고 써 버려, 아이 테이블의 요소가 shop_params . .
    f.fields_for 라고 기재하는 것으로, 부모 테이블의 폼 오브젝트에 연결할 수가 있습니다.

    테스트



    테스트도 나름대로 요령이 필요했기 때문에 기재합니다. 여기에서는 요청 spec만을 작성합니다.

    factory



    부모(shop)



    spec/factories/shops.rb
    FactoryBot.define do
      factory :shop do
        name { "テストのお店" }
    
        trait :with_nested_instances do
          after( :create ) do |shop|
            create :comment, id: shop.id
          end
        end  
      end
    end
    
    trait 라고 하는 것은, FactoryBot로, 상황에 따라 조금만 다른 데이터를 이용하고 싶을 경우의 설정 방법입니다.
    after(:create) 는 FactoryBot 콜백입니다. 이 근처는 이 기사 가 참고가 되었습니다.

    아이(comment)



    spec/factories/comments.rb
    FactoryBot.define do
      factory :comment do
        content { "コメント" }
      end
    end
    

    자식 모델의 factory에 특별한 변화는 없습니다.

    요청 spec



    spec/requests/shops_request_spec.rb
    RSpec.describe "Shops", type: :request do
    
      describe "POST /shops" do
        context "依存関係のあるテーブルのparamsが送信されているとき" do
        before do
            @comment_params = {
              comments_attributes: {
                "0": FactoryBot.attributes_for(:comment)
              }
            }
            @params_nested = { 
              shop: FactoryBot.attributes_for(:shop).merge( @comment_params )
            }
          end
    
          it 'リクエストが成功すること' do
            post shops_url, params: @params_nested
            expect(response.status).to eq 302
          end
    
          it "ショップ情報とコメントが新規作成されること" do
            expect do
              post shops_url, params: @params_nested
            end.to change(Shop, :count).by(1) and change(Comment, :count).by(1)
          end
        end
      end
    end
    
    "0" 근처가 조금 특수한 쓰는 방법으로 보입니다만, 이것은, 실제의 데이터 송신시에 보내지는 params 에 맞춘 형태가 됩니다. 길었지만 테스트까지 노력한 덕분에 의존성이 있는 데이터의 작성과 오이타 사이 좋게 되었습니다.

    참고 기사



    여기까지 참고로 한 기사는 이쪽입니다.
    RSpec에서 FactoryGirl 사용법 요약
    【Rails】 복수의 레코드를 작성한다. 모델 관계에 따라 다른 form_for/fields_for 사용법
    ↓특히 이 기사에는 신세가 되었습니다! !
    [Rails] accepts_nested_attributes_for 사용법

    추가 (발전 학습?)



    결국은 자신의 범 미스였지만, 마찬가지로 중첩된 테이블에 대해 고민하고 있을 때 학교의 동창생 커뮤니티에 투고하면 다음을 가르쳐 주셨습니다.

    ▼잘 모르겠지만 폼이 깔끔하게 쓸 수 있는 것 같다(그런 이해로 죄송합니다)
    【Rails】FormObject를 사용해주세요.

    ▼다음의 폼에는 사용해 보고 싶다고 생각하고 있는 gem, 1체다의 구조를 가지는 테이블로, 다수의 아이 테이블 데이터를 단번에 작성할 수 있는 녀석
    fields_for를 사용한 아이 모델에 복수 레코드 보존 【cocoon이 편리】

    그럼 오늘도 앞으로 일, 노력하겠습니다. 조언 해 주신 여러분, 감사합니다! !

    좋은 웹페이지 즐겨찾기