Many to many relationships N:M or M:N
한 테이블의 0 개 이상의 레코드가 다른 테이블의 0개 이상의 레코드와 관련된 경우
→양쪽 모두에게 N:1 관계를 가짐
N:1의 한계 (예시로 설명)
- 한 명의 의사에게 여러 환자가 예약할 수 있도록 설계
- 2명의 의사와 환자를 생성하고 환자는 서로 다른 의사에게 예약
- 만약 1번 환자가 두 의사 모두에게 진료를 받고자 한다면 환자 테이블에 1번 환자 데이터가 중복으로 입력됨
외래 키 컬럼에 '1,2' 형태로 저장하는 것은 DB 타입 문제로 불가능하기 때문에 중개 모델(예약 테이블)을 따로 만들어서 해결해야함
중개 모델
1. 예약 모델 생성
- 환자 모델의 외래 키를 삭제하고 별도의 예약 모델을 새로 생성
- 예약 모델은 의사와 환자에 각각 N:1 관계를 가짐
2. 예약 데이터 생성
- 데이터베이스 초기화 후 Migration 진행 및 shell_plus 실행
- 의사와 환자 생성 후 예약 만들기
3. 예약 정보 조회
- 의사와 환자가 예약 모델을 통해 각각 본인의 진료 내역 확인
의사 → 예약 정보 찾기
환자 → 예약 정보 찾기
4. 추가 예약 생성
5. 예약 정보 조회
의사 → 환자 목록
Django 에서는 'ManyToManyField'로 중개모델을 자동으로 생성
ManyToManyField()
M:N 관계 설정 모델 필드
1. Django ManyToManyField
- 환자 모델에 ManyToManyField 작성
- 의사 모델에 작성해도 상관 없으며 참조/역참조 관계만 잘 기억 할 것!!!
- 다대다 관계는 복수형으로 하는 것이 좋다(doctors)
2. Django ManyToManyField
- migration 실행후 생성된 중개 테이블 hospitals_patient_doctors 확인
3. Django ManyToManyField
- 의사 1명과 환자 2명 생성
4. Django ManyToManyField
- 예약 생성 (환자가 예약)
patient1이 doctor1에게 예약
patient1 - 자신이 예약한 의사목록 확인
doctor1 - 자신이 예약한 환자목록 확인
5. Django ManyToManyField
- 예약 생성(의사가 생성)
doctor1이 patient2을 예약
doctor1 - 자신의 예약 환자목록 확인
patient1, 2 - 자신이 예약한 의사목록 확인
6. Django ManyToManyField
- 중개 테이블에서 예약 현황 확인
7. Django ManyToManyField
- 예약 취소하기 (삭제)
- 이전에는 Reservation을 찾아서 지워야 했다면, 이제는 .remove()로 삭제 가능
doctor1이 pation1 진료 예약 취소
patient2가 doctor2 진료 예약 취소
'through' argument
중개 테이블에 '추가 데이터'를 사용해 M:N 관계를 형성하려는 경우에 사용
'through' argument (1/6)
- Reservation Class 재작성 및 through 설정
- 이제는 예약 정보에 "증상"과 "예약일" 이라는 추가 데이터가 생김
'through' argument (2/6)
- 데이터베이스 초기화 후 Migration 진행 및 shell_plus 실해
- 의사 1명과 환자 2명 생성
'through' argument (3/6)
- 예약 생성 방법 - 1
- Reservation class를 통한 예약 생성
'through' argument (4/6)
- 예약 생성 방법 - 2
- Patient 또는 Doctor의 인스턴스를 통한 예약 생성(through_defaults)
'through' argument (5/6)
- 생성된 예약 확인
'through' argument (6/6)
- 생성과 마찬가지로 의사와 환자 모두 각각 예약 삭제 가능
M:N 관계 주요 사항
- M:N 관계로 맺어진 두 테이블에는 물리적인 변화가 없음
- ManyToManyField는 중개 테이블을 자동으로 생성
- ManyToManyField는 M:N 관계를 맺는 두 모델 어디에 위치해도 상관 없음
- 대신 필드 작성 위치에 따라 참조와 역참조 방향을 주의할 것
- N:1은 완전한 종속 관계였지만 M:N은 종속적인 관계가 아니며 '의사에게 진찰받는 환자 & 환자를 진찰하는 의사' 이렇게 2가지 형태 모두 표현 가능
ManyToManyField() 자세히 보기
ManyToManyField(to, **options)
- M:N 관계 설정 시 사용하는 모델 필드
ManyToManyField 특징
- 양방향 관계
- 어느 모델에서든 관련 객체에 접근할 수 있음
- 중복 방지
- 동일한 관계는 한 번만 저장됨
ManyToManyField의 대표 인자 3가지
- related_name
- symmetrical
- through
1. 'related_name' arguments
- 역참조시 사용하는 manager name을 변경
- 의사 → 환자 (역참조)
- doctor.patient_set.메서드() 를 똑같이 doctor.patients.메서드() 처럼 맞춰주고 싶을 때 사용
2. 'symmetrical' arguments (1/2)
- 관계 설정 시 대칭 유무 설정
- ManyToManyField가 동일한 모델을 가리키는 정의에서만 사용
- 기본 값:True
- True일 경우
- source 모델의 인스턴스가 target 모델의 인스턴스를 참조하면 자동으로 target 모델 인스턴스도 source 모델 인스턴스를 자동으로 참조하도록 함(대칭)
- source 모델: 관계를 시작하는 모델
- target 모델 : 관계의 대상이 되는 모델
- 즉, 내가 당신의 친구라면 자동으로 당신도 내 친구가 됨
- source 모델의 인스턴스가 target 모델의 인스턴스를 참조하면 자동으로 target 모델 인스턴스도 source 모델 인스턴스를 자동으로 참조하도록 함(대칭)
- False일 경우
- True와 반대 (대칭되지 않음)
3. 'through' arguments
- 사용하고자 하는 중개모델을 지정
- 일반적으로 "추가 데이터를 M:N 관계와 연결하려는 경우"에 활용
M:N에서의 대표 조작 methods
- add()
- 관계 추가
- "지정된 객체를 관련 객체 집합에 추가"
- remove()
- 관계 제거
- "관련 객체 집합에서 지정된 모델 객체를 제거"
좋아요 기능 구현
Article(M) - User(N)
0개 이상의 게시글은 0명 이상의 회원과 관련
→ 게시글은 회원으로부터 0개 이상의 좋아요를 받을 수 있고, 회원은 0개 이상의 게시글에 좋아요를 누를 수 있음
모델 관계 설정 (1/4)
- Article 클래스에 ManyToManyField 작성
모델 관계 설정 (2/4)
- 현재 Migration 진행 시 에러 발생
- 게시글 작성한 유저 : Article - User (N:1) - N:1에서의 역참조 (U → A)
- user.article_set
- 게시글 좋아요한 유저 : Article - User (N:M) - N:M에서의 역참조 (U → A)
- user.article_set
- related_name argument가 서로 동일해 충돌이 났기 때문에 발생
- 해결방법
- 역참조의 이름을 바꾸기
- 다대다 필드를 user로 옮기기
역참조 매니저 충돌(1/2)
- N:1
- "유저가 작성한 게시글"
- user.article_set.all()
- M:N
- "유저가 좋아요한 게시글"
- user.article_set.all()
역참조 매니저 충돌(2/2)
- like_users 필드 생성 시 자동으로 역참조 매니저 article_set가 생성됨
- 그러나 이전 N:1 ( Article - User ) 관계에서 이미 같은 이름의 매니저를 사용중
- user.article-set.all() → 해당 유저가 작성한 모든 게시글 조회
- 'user가 작성한 글 (user.article_set)'과 'user가 좋아요 누른 글(user.article_set)'을 구분할 수 없게 됨
- user와 관계된 ForeignKey 혹은 ManyToManyField 둘 중 하나에 related_name 작성 필요
모델 관계 설정 (3/4)
- related_name 작성 후 Migration 재진행
모델 관계 설정 (4/4)
- 생성된 중개 테이블 확인
User - Article간 사용 가능한 전체 related manager
- article.user
- 게시글을 작성한 유저 - N:1
- user.article_set
- 유저가 작성한 게시글(역참조) - N:1
- article.like_users
- 게시글을 좋아요 한 유저 -M:N
- user.like_articles
- 유저가 좋아요 한 게시글(역참조) - M:N
기능 구현(1/5)
- url 작성
기능 구현(2/5)
- view 함수 작성
기능 구현(3/5)
- index 템플릿에서 각 게시글 좋아요 버튼 출력
기능 구현(4/5)
- index 템플릿에서 각 게시글 좋아요 버튼 출력
기능 구현(5/5)
- 좋아요 버튼 클릭 후 테이블 확인
'Django' 카테고리의 다른 글
DRF 01 [Django] (0) | 2024.10.16 |
---|---|
Many to many relationships 2 [Database] (1) | 2024.10.15 |
Many to one relationships 2 [Database] (0) | 2024.10.11 |
Many to one relationships 01 [Database] (1) | 2024.10.11 |
SQL 02 [Database] (0) | 2024.10.10 |