rails5.1에서 1:N 관계형 모델 만들기

Posted by negabaro kim on Thursday, March 15, 2018 Tags: rails handson   5 minute read

1:N관계란

image

고객(customer)1명이 복수의 주문(order)을 할 수 있고 주문은 반드시 어떤 한명의 고객에 의해 주문되어지므로 customer와order는 1:N관계라고 말할 수 있습니다.

프로젝트 작성

rails new rails-relation-1-n
cd rails-relation-1-n

모델&테이블 작성

Customer모델과customers테이블을 작성해줍니다.

rails g model Customer name:string
rake db:migrate

Order모델과orders테이블을 작성해줍니다.

rails g model Order order_date:date
rake db:migrate

참조키 추가해주기

N에 해당하는 테이블(현재는 order테이블)에 xxx_id라는 외부키를 추가해줍니다. xxx는 1에 해당하는 테이블(customer)의 모델명을 넣어줄 필요가 있습니다.

기존의 모델이 있는 상태에서 컬럼을 추가해줄때는 이하와같은 방법으로 추가해줄 수 있습니다.

rails g migration add_customer_id_to_orders customer_id:integer
rake db:migrate

사실 이전 Order모델 작성시

rails g model Order customer_id:integer order_date:date

이렇게 적어도 상관없습니다.

각모델에 has_many와belogns_to 설정해주기

각 모델간의 관계를 선언하는 방법인 has_many와 belongs_to 메소드를 사용방법을 배워보겠습니다.

has_many는 1에 해당하는 모델(customer)에다가 복수의order를 가진다는 의미로 has_many를 선언합니다. 반대로 belongs_to는 N에 해당하는 모델(order)에다가 특정 테이블에 종속된다는 의미로 belongs_to를 선언해줘야합니다.

# app/models/customer.rb
class Customer < ActiveRecord::Base
  has_many :orders
end
# app/models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer
end

has_many와belongs_to에는 각각 옵션을 지정할 수 있는데요. 다음과 같습니다.

has_many의 옵션들

Option name Description
class_name 관계되는 모델의 클래스명을 지정가능 관계명과 참조하는 모델의 클래스명을 달리 쓰고싶을때 사용가능
foreign_key 외부키의 이름을 지정가능 default는 참조하는 모델명_id
dependent 부모 오브젝트가 삭제될때의 액션을 정의 destroy,delete_all등 지정가능
as polymorphic관계를 정의
through 모델관계의 설정이가능

belongs_to의 옵션들

Option name Description
class_name 관계되는 모델의 클래스명을 지정가능 관계명과 참조하는 모델의 클래스명을 달리 쓰고싶을때 사용가능
foreign_key 외부키의 이름을 지정가능 default는 참조하는 모델명_id
dependent 부모 오브젝트가 삭제될때의 액션을 정의 destroy,delete등을 지정가능
polymorphic polymorphic관계를 정의

사용가능한 커맨드 일람

위의 옵션을 설정하면 아래와 같은 커맨드들을 자동으로 쓸 수 있게됩니다.

생성,저장 관련

sana = Customer.create(name: "minatozaki") # 미나토자키라는 이름을 가진 고객을 만들어서 DB저장후 sana객체에 대입
order1 = sana.orders.build(order_date: Time.now) # order1작성 (new대신에build를 사용)
order1.save # order1 DB저장

order2 = sana.orders.create(order_date: Time.now) # order2만들고 DB저장까지

order3 = sana.orders.new(order_date: Time.now)  #order3작성 (요번엔 build대신 new사용 둘이 똑같음)
order3.save # order3 DB저장

릴레이션 관련

sana.orders         # => order오브젝트의 배열을 리턴
sana.orders.exists? # => true (존재하는지 판별)
sana.orders.empty?  # => false (값이 있는지 판별)
order1.customer     # => customer오브젝트(해당 주문을 소유하고 있는 고객의 정보)

sana.orders안의 레코드에서 검색

sana.orders.find(...)
sana.orders.find_by(...)
sana.orders.where(...)

삭제

Order.count # => 사나가 구입한 주문갯수
sana.destory # => 모델의 옵션에 dependent: :destroy가 지정되 있으면 、sana가 주문한 내용도 다삭제됨.
Order.count # => dependent: :destroy

belongs_to설정을 삭제하면 위에서 설명한 커맨드중 어느부분을 사용할 수 없을까

위에서 설명한 사용가능한 커맨드 일람을 만져 보면서 어느 부분이 has_many나 belongs_to에 의존된 설정인건지 둘이 무조건 셋트로 지정해야하는건지 궁금했기에 설정을 삭제해보고 해당 커맨드를 다시 실행해봤습니다.

※설정 변경후 rails console재접속 해야합니다.

app/models/customer.rb

has_many :orders

를 삭제후 이하와 같은 커맨드를 실행하면

Customer.create(name: "dahyun")
dubu = Customer.find_by(id: 2)
dubu.orders.create(order_date: Time.now)

하기와 같은 에러가 발생합니다.

NoMethodError: undefined method `orders' for #<Customer:0x007f7b6ff89930>
        from (irb):5

고객 오브젝트에서 주문정보에 억세스할 수 없게 되었습니다.

app/models/order.rb

belongs_to :customer 만 삭제하면 위와같은 에러가 나지 않았습니다.

order4= dubu.orders.build(order_date: Time.now)
order4.save

문제없시 성공

belongs_to :customer을 삭제하게되면

order = Order.find_by(id: 1)
order.customer

실행결과(에러발생):

NoMethodError: undefined method `customer' for #<Order:0x007f7b702ffbc8>
Did you mean?  customer_id
        from (irb):2

위와같이 주문 오브젝트에서 고객정보로 억세스가 안되게 됩니다.

정리

고객(1) -> 주문(N) 의 관계를 명시하기 위해

app/models/customer.rbhas_many를 정의

has_many를 정의해주지 않으면

고객 오브젝트를 통해서 주문정보에 악세스할 수없습니다.

예)  sana.orders.build(order_date: Time.now) # 이런게 안됨

고객(1) <- 주문(N) 의 관계를 명시하기 위해

app/models/order.rbbelongs_to를 정의

belongs_to를 정의해주지 않으면

주문 오브젝트를 통해서 고객정보에 악세스할 수 없게됩니다.

예) order1.customer # 이런게 안됨