Django

Many to many relationships 2 [Database]

경상도상남자 2024. 10. 15. 12:07

팔로우 구현하기

사전 준비 : 프로필 페이지 준비하기

 

프로필 구현(1/5)

  • url 작성

accounts/urls.py

프로필 구현(2/5)

  • view 함수 작성

 

프로필 구현(3/5)

  • profile 템플릿 작성

 

 

 

프로필 구현(4/5)

  • 프로필 페이지로 이동할 수 있는 링크 작성

articles/index.html
articles/index.html

프로필 구현(5/5)

  • 프로필 페이지 결과 확인
  •  

index페이지
profile페이지

 

 

 

모델 관계 설정

User(M) - User(N)

  • 0명 이상의 회원은 0명 이상의 회원과 관련
  • 회원은 0명 이상의 팔로워를 가질 수 있고, 0명 이상의 다른 회원들을 팔로잉 할 수 있음

모델 관계 설정 (1/2)

ManyToManyField 작성

 

  • 참조
    • 내가 팔로우 하는 사람들 (팔로잉, followings)
  • 역참조
    • 상대방 입장에서 나는 팔로워 중 한 명 (팔로워, followers)

바뀌어도 상관 없으나 관계 조회 시 생각하기 편한 방향으로 정할 것

 

모델 관계 설정 (2/2)

  • Migrations 진행 후 중개 테이블 확인

 

 

 

기능 구현

 

기능 구현(1/4)

 

  • url 작성

accounts/urls.py

 

기능 구현(2/4)

    • view 함수 작성

 

기능 구현(3/4)

  • 프로필 유저의 팔로잉, 팔로워 수 & 팔로우, 언팔로우 버튼 작성

 

기능 구현(1/4)

 

 

Fixtures

Django 가 데이터베이스로 가져오는 방법을 알고 있는 데이터 모음

 

→ 데이터는 데이터베이스 구조에 맞추어 작성 되어 있음

 

사용 목적

  • 초기 데이터 제공

 

초기 데이터의 필요성

  • 프로젝트의 앱을 처음 설정할 때 동일하게 준비 된 데이터로 데이터베이스를 미리 채우는 것이 필요한 순간이 있음

→ Django 에서는 fixtures을 사용해 앱에 초기 데이터(initial data)를 제공

 

 

fixtures 관련 명령어

 

  • dumpdata : 생성(데이터 추출)
  • loaddata : 로드 (데이터 입력)

dumpdata

데이터베이스의 모든 데이터를 추출

 

 

모든 모델을 한꺼번에 dump 하기

  • 데이터 관리가 어렵기 때문에 권장하지는 않는다.

 

 

Fixtures 파일을 직접 만들지 말 것!!

 

loaddata

Fixtures 데이터를 데이터베이스로 불러오기

 

약속된 load하는 경로

 

Fixtures 파일 기본 경로

 

  • app_name/fixtures

→ Django는 설치된 모든 app의 디렉토리에서 fixtures 폴더 이후의 경로로 fixtures 파일을 찾아 load

 

실행

 

loaddata 순서 주의사항

 

  • 만약 loaddata를 한번에 실행하지 않고 별도로 실행한다면 모델 관계에 따라 load 순서가 중요할 수 있음
    • comment는 article에 대한 key 및 user에 대한 key가 필요
    • article은 user에 대한 key가 필요
  • 즉, 현재 모델 관계에서는 user → article → comment 순으로 data를 load 해야 오류가 발생하지 않는다

 

loaddata 시 encoding codec 관련 에러가 발생하는 경우 해결방법!!

 

  1. 메모장 활영
    1. 메모장으로 json 파일 열기
    2. "다른 이름으로 저장" 클릭
    3. 인코딩을 UTF8로 선택 후 저장 
  2. dumpdata 시 추가 옵션 작성

 

 

Improve query

"query 개선하기"

 

→ 같은 결과를 얻기 위해 DB 측에 보내는 query 개수를 점차 줄여 조회하기

 

 

annotate

  • SQL의 GROUP BY를 사용
  • 쿼리셋의 각 객체에 계산된 필드를 추가
  • 집계 함수(Count, Sum 등)와 함께 자주 사용됨

annotate 예시

  • 의미
    • 결과 객체에 'num_authors' 라는 새로운 필드를 추가
    • 이 필드는 각 책과 연관된 저자의 수를 계산
  • 결과
    • 결과에는 기존의 필드와 함계 'num_authors' 필들를 가지게 됨
    • book.num_authors로 해당 책의 저자 수에 접근할 수 있게 됨

 

annotate 적용해보기

Django 디버크 툴바 확장프로그램 활용

문제 상황 

 

모든 게시글을 출력하는데 + 댓글 개수도 같이 출력

→ 각 게시글마다 댓글 개수를 반복 평가하기 때문에  이 데이터를 얻기 위해 총 11번의 SQL 문을 보냄

 

 

문제해결

게시글을 조회하면서 댓글 개수까지 한번에 조회해서 가져오기

 

 

article.comment__count : 실제로 존재하는 테이블이 아니라 이름을 지정하지 않으면 이름이 이상하게 만들어짐

select_related

  • SQL의 INNER JOIN을 사용
  • 1:1 또는 N:1 참조 관계에서 사용
    • ForeignKey나 OneToOneField 관계에 대해 JOIN을 수해
  • 단일 쿼리로 관련 객체를 함께 가져와 성능을 향상

select_related 예시

 

  • 의미
    • Book 모델과 연관된 Publisher 모델의 데이터를 함께 가져옴
    • ForeignKey 관계인 'publisher'을 JOIN하여 단일 쿼리 만으로 데이터를 조회
  • 결과
    • Book 객체를 조회할 때 연관된 Publisher 정보도 함께 로드
    • book.publisher.name과 같은 접근이 추가적인 데이터베이스 쿼리 없이 가능

select_related 적용해보기

Django 디버크 툴바 확장프로그램 활용

문제 상황 

 

모든 게시글을 출력하는데 + 각각의 게시글에 작성자 이름도 출력되고 있음

→ 각 게시글마다 댓글 개수를 반복 평가하기 때문에  이 데이터를 얻기 위해 총 11번의 SQL 문을 보냈는데 10개가 비슷하고 8개가 중복인 상황

문제해결

문제 원인 

  • 각 게시글마다 작성한 유저명까지 반복 평가

 

게시글을 조회하면서 유저 정보까지 한번에 조회해서 가져오기

 

prefetch_related

  • SQL이 아닌 Python을 사용한 JOIN을 진행
    • 관련 객체들을 미리 가져와 메모리에 저장하여 성능을 향상
  • M:N 또는 N:1 역참조 관계에서 사용
    • ManyToManyField나 역참조 관계에 대해 별도의 쿼리를 실행

 

prefetch_related 예시

 

  • 의미
    • Book과 Author는 ManyToMany 관계로 가정
    • Book 모델과 연관된 모든 Author 모델의 데이터를 가져옴
    • Django가 별도의 쿼리로 Author 데이터를 가져와 관계를 설정
  • 결과
    • Book 객체들을 조회한 후, 연관된 모든 Author 정보가 미리 로드 됨
    • for author in book.authors.all()와 같은 반복이 추가적인 데이터 베이스 쿼리 없이 실행됨

prefetch_related 적용해보기

문제 상황 

 

모든 게시글을 출력하는데 + 각각의 게시글에 작성된 댓글의 목록도 출력되고 있음

→ 각 게시글마다 댓글 개수를 반복 평가하기 때문에  이 데이터를 얻기 위해 총 11번의 SQL 문을 보냈는데 10개가 비슷한 상황

문제해결

문제 원인 

  • 각 게시글 출력 후 각 게시글의 댓글 목록까지 개별적으로 모두 평가

 

게시글을 조회하면서 참조된 댓글까지 한번에 조회해서 가져오기

 

select_related & prefetch_related 적용해보기

 

문제 상황 

 

 

모든 게시글을 출력하는데 + 각각의 게시글에 작성된 댓글의 목록 + 거기에 각각의 게시글 댓글의 작성자 이름도 출력되고 있음

→ 각 게시글마다 댓글 개수를 반복 평가하기 때문에  이 데이터를 얻기 위해 총 111번의 SQL 문을 보냈는데 100개가 비슷한 상황

 

문제 해결

문제 원인

  • "게시글" + "각 게시글의 댓글 목록" + "댓글의 작성자"를 단계적으로 평가하고 있음
  •  

 

  1. "게시글(1)" → "각 게시글의 댓글 목록(N)" = prefetch_related
  2. "각 게시글의 댓글 목록(N)" + "댓글의 작성자(1)" = select_related로 해결

 

 

 

섣부른 최적화는 악의 근원!!

"작은 효율성에 대해서는, 말하자면 97% 정도에 대해서는, 잊어버려라. 섣부른 최적하는 모든 악의 근원이다
- 도널드 커누스(Donald E. Knuth)

 

참고

'exist' method

.exists()

  • QuerySet에 결과가 하나 이상 존재하는지 여부를 확인하는 메서드
  • 결과가 포함되어 있으면 True를 반환하고 결과가 포함되어 있지 않으면 False를 반환함

특징

  • 데이터베이스에 최소한의 쿼리만 실행하여 효율적
  • 전체 QuerySet을 평가하지 않고 결과의 존재 여부만 확인

→ 대량의 QuerySet에 있는 특정 객체 검색에 유용

 

적용 예시

'Django' 카테고리의 다른 글

DRF02-with N:1 Relation [django]  (0) 2024.10.17
DRF 01 [Django]  (0) 2024.10.16
Many to Many Relationships 01 [Database]  (1) 2024.10.14
Many to one relationships 2 [Database]  (0) 2024.10.11
Many to one relationships 01 [Database]  (1) 2024.10.11