Many to one relationships 1

뚝딱뚝딱·2023년 10월 11일
0

DB

목록 보기
2/5
post-thumbnail

개요

Many to one relationships (N:1 or 1:N)

  • 한 테이블의 0개 이상의 레코드가 다른 테이블의 레코드 한 개와 관련된 관계

Comment(N)-Article(1)

  • 0개 이상의 댓글은 1개의 게시 글에 작성 될 수 있다.

테이블 관계

ForeignKey()

  • N:1 관계 설정 모델 필드

댓글 모델 구현

댓글 모델 정의

  • ForeignKey() 클래스의 인스턴스 이름은 참조하는 모델 클래스 이름의 단수형으로 작성하는 것을 권장
  • ForeignKey 클래스를 작성하는 위치와 관계없이 외래 키는 테이블 필드 마지막에 생성됨

ForeignKey(to, on_delete)

  • to : 참조하는 모델 class 이름
  • on_delete : 외래 키가 참조하는 객체(1)가 사라졌을 때, 외래 키를 가진 객체(N)를 어떻게 처리할 지를 정의하는 설정(데이터 무결성)

on_delete의 'CASCADE'

Migration

  • 댓글 테이블의 article_id 필드 확인
  • 참조하는 클래스 이름의 소문자(단수형)로 작성하는 것이 권장 되었던 이유
    - 참조 대상 클래스 이름+ _ + 클래스 이름

댓글 생성 연습

# 게시글 생성
Artilcle.objects.create(title='title', content='content')

# Comment 클래스의 인스턴스 comment 생성
comment = Comment()

# 인스턴스 변수 저장
comment.content = 'first comment'

# DB에 댓글 저장
comment.save()

# 에러 발생
django.db.utils.IntegrityError: NOT NULL constraint failed:articles_comment.article_id
# articles_comment테이블의 ForeignKeyField, article_id 값이 저장 시 누락되었기 때문!!

# 게시글 조회
article = Article.objects.get(pk=1)

# 외래 키 데이터 입력
comment.article = article
# 또는 comment.article_id = article.pk처럼 pk 값을 직접외래 키 컬럼에 넣어줄수도 있지만 권장하지 않음

# 댓글 저장 및 확인
comment.save()
comment.pk => 1
comment.content => 'first comment'

# 클래스 변수명인 article로 조회 시 해당 참조하는 게시물 객체를 조회할 수 있음
comment.article => <Article: Article object (1)>

# article_pk는 존재하지 않는 필드이기때문에 사용 불가
comment.article_id => 1

# 1번 댓글이 작성된 게시물의 pk 조회
comment.article.pk => 1

# 1번 댓글이 작성된 게시물의 content 조회
comment.article.content => 'content'

sell_plus 실행 및 게시글 작성 python manage.py shell_plus

오류 발생 !! IntegrityError: NOT NULL constraint failed: articles_comment.article_id : 제약조건을 통과하지 못했다. 테이블이름.필드이름 => 댓글테이블의 외래키 필드에 해당하는 article_id가 누락되었다는 뜻이다.

이렇게 하면 오류 안남
외래 키 데이터 입력 댓글 저장 및 확인
댓글 하나 더 작성하기
처음에 오류냄 -> comment 객체가 Comment 클래스로 만들어진건데 그 객체가 Comment를 호출하는건 이상하지 않을까요ㅎㅎ

관계 모델 참조

N -> 1 (참조) : 댓글이 어떤 게시글에 작성되었는지를 조회 가능
1 -> N (역참조) : 해당 게시글에 작성된 모든 댓글을 조회

역참조

  • N:1 관계에서 1에서 N을 참조하거나 조회하는 것 ( 1 -> N)
  • N은 외래 키를 가지고 있어 물리적으로 참조가 가능하지만 1은 N에 대한 참조 방법이 존재하지 않아 별도의 역참조 이름이 필요

article.comment_set.all()

article.comment_set.all()
= 모델 인스턴스.related manager(역참조 이름).QuerySet API

  • 장고가 알아서 생성해줌 우리가 고려할 필요 없음
  • N:1 혹은 M:N 관계에서 역참조 시에 사용하는 매니저
  • 'objects' 매니저를 통해 queryset api를 사용했던 것처럼 related manager를 통해 queryset api를 사용할 수 있게 됨

이름 규칙

  • N:1 관계에서 생성되는 Related managr의 이름은 참조하는 "모델명_set" 이름 규칙으로 만들어짐
  • 해당 댓글의 게시글(Commnet -> Article)
    - comment.article
  • 게시글의 댓글 목록(Article -> Comment)
    - article.comment_set.all()
  • shell_plus 실행 및 1번 게시글 조회
article = Article.objects.get(pk=1)
  • 1번 게시글에 작성된 모든 댓글 조회하기(역참조)
article.comment_set.all()
1번 게시글에 작성된 모든 댓글 내용 출력

댓글 구현

댓글 CREATE 구현

사용자로부터 댓글 데이터를 입력 받기 위한 CommentForm정의

detail view 함수에서 CommentForm을 사용하여 detail 페이지에 렌더링

- 댓글 이렇게 안쓰잖아
- Comment 클래스의 외래 키 필드 article 또한 데이터 입력이 필요한 필드이기 때문에 출력 되고 있는 것
- 하지만, 외래 키 필드는 사용자 입력 값으로 받는 것이 아닌 view 함수 내에서 다른 방법으로 전달 받아 저장되어야 함

content만 나오게 CommentForm의 출력 필드 수정
  • 출력에서 제외된 외래 키 데이터는 어디서 받아와야 할까??
  • detail 페이지의 url
    - path('<int:pk>/', views.detial, name='detail')에서 해당 게시글의 pk값이 사용되고 있다
  • 댓글의 외래 키 데이터에 필요한 정보가 게시글의 pk값
url 작성 및 action 값 작성

comments_create view 함수 정의

save(commit=False)

  • DB에 저장하지 않고 인스턴스만 반환
save 의 commit 인자를 활용해 외래 키 데이터 추가 입력

댓글 READ

detail view 함수에서 전체 댓글 데이터를 조회

전체 댓글 출력 및 확인

댓글 DELETE

댓글 삭제 url 작성
댓글 삭제 view 함수 정의
댓글 삭제 버튼 작성
댓글 삭제 버튼 출력 확인 및 삭제 테스트
댓글2 삭제됨

참고

Comment 모델을 admin site에 등록해 CRUD 동작 확인하기

from .models import Article, Comment
admin.site.register(Article)
admin.site.register(Comment)

댓글이 없는 경우 대체 콘텐츠 출력

  • DTL 'for empty' 태그 사용

댓글 개수 출력하기
1. DTL filter='length' 사용

{{ comments|lengh }}
{{ article.comment_set.all|length }}
  1. Queryset API - 'count()'사용
{{ article.comment_set.count}}
profile
나의 행복 만들기

0개의 댓글