TIL DAY 29 || Django Foreign key constraint failure on get_or_create()

TK·2021년 3월 27일
0

TIL

목록 보기
36/55

프로젝트 중 fk 때문에 발생한 에러를 수도 없이 봐왔지만, 이번 에러에선 특히 배울점이 매우 많다고 생각해서 포스팅해본다.

Foreign key constraint on get_or_create()

처음에 get_or_create() 를 보고 신세계라고 생각하고 편하게 쓰고 있던 중 갑자기 foreign key 에러가 발생했다.

에러명은 다음과 같은 식이였다.

FOREIGN KEY CONSTRAINT FAILS (5-8-2)

처음에 든 생각은,

  • get_or_create() 에서 에러가 왜 나지 ?
  • 있는 컬럼이면 새로 만들지 않고 get 하고
  • 없는 컬럼이면 새로 create 하는데 어떻게 에러가 나지 ?

라는 생각이였다.

그래서 한참을 헤매다가 그 이유를 알게되었다.

  • 먼저 cart 에 상품을 담고, 이미 있는 상품이면 담았던 quantity 만큼 추가해주는 로직이였다.
  • 그래서 product_id, order_id, product_option_id, quantity 를 get_or_create() 의 인자로 넘겨줘서, 없으면 새로 row 를 만들고 있으면 quantity 만 추가하기로 했었다.

바로 여기서 문제가 시작된다.

  • A 라는 특정상품이 3개만큼 담겨있었는데, 사용자가 A 라는 상품 4 개를 추가한다면 어떻게 될까.
  • get_or_create() 로직은 먼저product_id, order_id, product_option_id, quantity 를 비교할것이다.
  • 이 때 product_id, order_id, product_option_id 세개의 값은 같지만 quantity
    가 다르기 때문에 새로운 row 를 생성하려고 할 것이다.
  • 새로운 row 를 생성하려고 했는데, product_id, order_id, product_option_id 의 값이 unique 하게 묶여있었기 때문에 에러를 뱉는다.

quantity 를 제외한 세 컬럼의 값을 models.py 에서 unique_together 로 묶어주었기 때문에 에러가 난것이다.

그렇다면 어떻게 해야 에러가 안날까 ?

답은 간단하다.

quantity 를 defaults 키값안에 딕셔너리 형태로 넘겨주면 된다.

cart, is_created = Cart.objects.get_or_create(
					      product_id        = product_id,
                                              order             = order,
                                              product_option_id = option['product_option_id']
                                              defaults          = {'quantity': option['product_option_quantity']}
                                              

이렇게 되면 defaults 값을 제외하고 검사한 뒤, 없으면 값을 defaults 에 입력된 값과 함께 생성하고 있으면 해당 row 를 가져온다.

How to debug FK constraint error on some other cases

다음은 다른 케이스에서 왜 fk constraint 에러가 났는지 디버깅하는 과정이다.
위에 get_or_create() 로 새로운 row 생성 시 발생한 에러들 처럼,

어떤 fk constraint 에러를 발생한 이유를 찾을 때 내가 디버깅하는 단계를
다른 케이스를 예시로 들어서 설명하려고 한다.

다음 에러메시지를 보자.

FOREIGN KEY ('product_option_id') REFERENCES 'product_options' ('id')

바로 이 부분인데, 여기서부터 디버깅을 시작하면 된다.

  • product_option_id 가 실제 product_options 테이블의 id 를 참조하려고 할 때 발생한 오류이다.
  • product_option_id 가 23 번이였는데, 위 테이블에 id 가 23 번인 row 는 어딜 봐도 없다.
  • product_option_id 를 임의로 집어넣는 과정에서 생긴 문제였다.
profile
Backend Developer

0개의 댓글