일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Service
- ansible
- AWS
- elasticsearch
- DevOps
- ebs
- IAC
- 자바스크립트
- Python
- FastAPI
- EKS
- asyncio
- github
- IAM
- WSGI
- terraform
- Django
- Kubernetes
- Deployment
- dockerfile
- POD
- leetcode
- YAML
- docker
- EC2
- K8S
- event loop
- asgi
- 쿠버네티스
- intervals
- Today
- Total
궁금한게 많은 개발자 노트
django model 간 관계 본문
테이블 간에는 관계를 맺을 수 있으며, django는 테이블 간의 관계를 3가지로 분류하여 제공 (1:N, N:N, 1:1)
첫째로, 관계라는 것은 양방향 개념으로 양쪽 모델에서 정의가 필요한게 원칙이지만, django에서는 한쪽 클래스에서만 관계를 정의하면 이를 바탕으로 상대편 정의는 자동으로 생성
두번째로, 한쪽 방향으로 관계를 생성하거나 변경하면, 반대 방향으로의 관계또 그에 다라 변한다는 것입니다.
따라서, 개발자는 한쪽 클래스에만 관계를 정의하거나 한쪽 방향으로의 관계 설정하는 것을 이해할 수 있어야 합니다.
[ N:1 관계 ]
테이블 간에 N:1관계를 맺기 위해서는 모델의 필드를 정의할 때 ForeignKey 필드 타입을 사용하면 됩니다.
ForeignKey타입은 관계를 맺고자 하는 모델 클래스를 필수 인자로 가집니다.
즉, N모델에서 ForeignKey필드를 정의하면서, 필수 인자로 1에 해당하는 모델 클래스를 지정하는 방식입니다.
# N모델에 명시적으로 외래키 정의, 상대 모델에서의 관계는 django에서 알아서 정의
# N:1관계에서는 1쪽의 객체를 지우면, CASCADE로 동작하여 N쪾의 객체도 삭제됩니다.
자식 객체는 db에 [부모_id]를 갖고 있기 때문에 직접 접근할 수 있지만, 기본적으로 부모 객체를 이용해
자식 객체를 찾을 수 없다. 이러한 역관계를 지원하기 위해 django에서는 RelatedManager를 제공
역관계를 정의하는 방법은 크게 두 가지가 있다.
1. [자식 모델의 소문자이름_set]을 이용(예: Post모델일 경우 -> post_set)
ex) user1.album_set.count() -> user1이 가진 album의 개수
user1.album_set.add(album객체)
-> user1의 N관계 객체로 album객체를 추가하고, album객체의 foreignKey에 user1이 추가됨
2. related_name설정 : 역참조 시 사용할 수 있는 이름 설정으로 ForeignKey설정의 optional 부분
class User(models.Model):
name = models.CharField(max_length = 50)
job = models.ForeignKey(
'Occupation',
on_delete = models.CASCADE,
related_name = 'appliers' ------------------------- [Key Point !]
)
created_at = models.DateTimeField(auto_now_add = True)
class Occupation(models.Model):
name = models.CharField(max_length = 50)
job1 = Occupation.objects.get(id = 1)
people = job1.appliers.all()
>>> <QuerySet[<Object User Object(1)>, <Object User Object(2)>]>
[ N:N 관계 ]
테이블 간에 N:N 관계를 맺기 위해서는 모델의 필드를 정의할 때 ManyToManyField 필드 타입을 사용하면 됩니다.
관계를 맺고자 하는 모델 클래스를 필수 인자로 사용하여 설정합니다. 필드 정의는 관계를 맺고자 하는 두 모델 클래스 중 한 곳에서만 설정해야 합니다 (나머지는 django에서 자동 설정)
한 곳의 객체를 삭제하더라도 다른 한쪽의 객체는 테이블 상에서 삭제되지 않음 (외래키 CASCADE 동작과는 다름)
[ 1:1 관계 ]
테이블 간에 1:1 관계를 맺기 위해서는 모델의 필드를 정의할 때 OneToOneFiled 필드 타입을 사용하면 됩니다.
관계를 맺고자 하는 모델 클래스를 필수 인자로 사용하여 설정합니다. (마찬가지로 한쪽에서만 정의)
개념적으로는 ForeignKey필드 타입에 unique=True옵션을 준 것과 비슷
다만 반대 방향의 동작은 다릅니다. 반대 방향의 객체는 복수 개의 객체를 반환하지만 1:1에서는 하나이기 때문
1:1 관계에서는 add() 메소드를 사용하지 않고, 바로 대입하는 형태로 사용
[ 관계 매니저 ]
Manager 클래스는 데이터베이스에 대한 처리, 즉 데이터베이스에 쿼리를 보내고 그 응답을 받는 역할을 합니다.
매니저 중에서 모델 간 관계에 대한 기능 및 데이터베이스 쿼리를 담당하는 클래스를 관계 매니저라고 합니다.
모델 간 관계를 쉽게 다루기 위한 클래스라고 생각하시면 됩니다.
N:1, N:N관계에서만 관계 매니저를 사용합니다. (1:1관계에서는 상대 객체가 하나이기 때문)
1:N관계에서 User:Album(1:N)의 예시로 들면 1에서 N방향 액세스는 ex) user1.album_set
album_set이 관계 매니저 클래스의 객체를 의미함
N에서 1방향으로 엑세스 하는 경우는 ForeignKey로 정의한 필드명을 사용하면
대상 객체가 하나뿐이므로 관계 매니저 사용 불필요
N:N관계에서는 관계 매니저 객체가 ManyToManyField 타입의 필드명으로 사용
관계 매니저 메소드
add : 인자로 주어진 모델 객체들을 관계 객체의 집합에 추가 (관계를 맺는 역할)
foreignKey관계에서는 관계 매니저는 자동으로 save()를 호출하여 데이터베이스를 업데이트(add의 대상이나 대입하여 넣어지는 객체에 대해 .save()호출), N:N에서는 관계 맺는 것도 add/대입으로 진행하고 QuerySet.bulk_create()메소드로 관계 형성하고 데이터 베이스에 저장 (save()호출X)
create : 새로운 객체를 생성해서 이를 데이터베이스에 저장하고, 관계 객체 집합에 넣습니다.
save()함수를 호출하지 않더라도 데이터베이스에 저장
remove : 인자로 지정된 모델 객체들을 관계 객체 집합에서 삭제합니다. 두 모델 객체 간의 관계를 해제
자동으로 save가 호출됩니다. (1:N경우 e.save()하지 않아도 됨)
N:N인 경우는 remove()를 사용하면 save가 아니라 QuerySet.delete()메소드를 사용하여 관계를 삭제
b = Blog.objects.get(id=1)
e = Entry.objects.get(id=234)
b.entry_set.remove(e) # Entry e 객체에서 Blog b 객체와의 관계를 끊음
e.blog = None과 동일한 효과이므로 ForeignKey인 경우 null=True인 경우만 가능
# 이 메소드는 bulk인자를 가지는데 (default True), 디폴트로 동작 시 QuerySet.update()메소드가 사용되고, false인 경우 모델 객체마다 save()가 호출됩니다. == QuerySet.update를 사용하는 것이 성능 개선의 효과
clear : 관계 객체 집합에 있는 모든 객체를 삭제. 해당 모델 객체가 맺고 있는 다른 객체들과의 모든 관계를 제거
remove() 메소드처럼 foreignKey로 사용시 null=True시에만 사용 가능, bulk인자에 따라 실행 방법이 상이
[ 참고 ]
위에서 설명한 add, create, remove, clear 메소드는 save()메소드 별도 호출 필요X
ForeignKey관계라면 null=True여부를 염두에 두고 개발 필요
QuerySet의 메소드(bulk_create, update, delete 등)를 사용하는 경우는 각 model의 instance를 돌면서 save()를 호출하지 않고 SQL Query문 한번에 처리하기 때문에 성능 개선의 효과가 있습니다.
하지만, 실제 데이터베이스에 바로 반영이 되므로 의도치 않은 데이터 가 들어가는 실수를 범할 수 있으므로 주의하여 사용하여야 합니다. (데이터 무결성이 깨지는 문제 발생 가능)
'Back End' 카테고리의 다른 글
[ Python ] 병렬 처리 concurrent future (4) | 2022.05.23 |
---|---|
Fast API (0) | 2022.05.23 |
django 핵심 기능 - Model (0) | 2022.05.19 |
django 개발의 기본 사항 (0) | 2022.05.19 |
토큰 기반 인증 시스템과 JWT (0) | 2022.04.19 |