M:N 관계
- M:N 관계(many to many relationship)는 한 테이블(A)의 한 행이 다른 테이블(B)의 여러 행과 관련이 있고, 반대로 한 테이블(B)의 한 행이 다른 테이블(A)의 여러 행과 관련이 있는 경우를 의미한다.
- 아래 예시에서
- 고객 테이블의 고객ID가 1인 행이 주문 테이블의 주문 ID가 101, 103인 행과 관련이 있고, 따라서 상품ID가 P001, P003인 행과 관련이 있다.
- 상품 테이블의 상품ID가 P001인 행이 주문 테이블의 주문 ID가 101, 104인 행과 관련이 있고, 따라서 고객ID가 1, 3인 행과 관련이 있다.
- 즉 고객 : 상품 = 1 : N, 상품 : 고객 = 1 : N 의 관계를 만족하는 경우 M:N 관계라고 한다.
- 고객 테이블
고객ID | 고객이름 | |
1 | 조나희 | nahee2253@nate.com |
2 | 조나단 | nahee2253@naver.com |
- 주문 테이블
주문ID | 고객ID | 상품ID | 날짜 |
101 | 1 | P001 | 2024-01-08 |
102 | 2 | P002 | 2024-01-09 |
- 상품 테이블
주문ID | 고객ID |
P001 | 노트북 |
P002 | 태블릿 |
중계 테이블
- 중계 테이블(association table)은 M:N 관계에서 두 테이블 간의 관계를 표현하기 위해 사용된다.
- 중계 테이블에는 두 테이블의 기본 키(PK)가 외래 키(FK)로서 포함된다.
- 위 예시의 주문 테이블 에는 고객 테이블과 상품 테이블의 PK가 FK로서 포함되고, 날짜 데이터가 추가되어 있다.
교사 - 학생의 관계에서
- 교사 - 학생은 어떤 관계를 가지고 있을까?
- 담임 교사의 관점
- 한 담임 교사는 여러 학생과 관계가 있다.
- 1 : N
- 한 학생은 한명의 담임 교사와 관계가 있다.
- 1 : 1
- → 담임 교사 - 학생의 관계는 1 : N 관계이다.
- 한 담임 교사는 여러 학생과 관계가 있다.
- 단순 교사의 관점
- 한 교사는 여러 학생과 관계가 있다.
- 1 : N
- 한 학생은 여러 교사와 관계가 있다.
- 1 : N
- → 교사 - 학생의 관계는 M : N 관계이다.
- 한 교사는 여러 학생과 관계가 있다.
- 담임 교사의 관점
- 추상화를 어떻게 하느냐에 따라, 즉 우리가 원하는 product에서 원하는 것이 무엇인지에 따라 사용하는 모델도 달라지게 된다.
- 즉, 기획은 구체적이어야 한다.]
[실습]
- 이전 실습에서 User - Article model간의 관계는 한명의 작성자(author)가 여러개의 Article을 작성할 수 있는 구조로, 1 : N 관계였다.
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
- User - Article은 해당 관계밖에 존재하지 않을까?
- User가 마음에 드는 Article들을 저장해두는 북마크 기능에 대해서
- 한명의 User가 여러 Article들을 북마크 할 수 있고
- 하나의 Article이 여러명의 User에게 북마크 될 수 있다
- User가 마음에 드는 Article들을 저장해두는 북마크 기능에 대해서
- M : N 관계에서 두개의 모델 중 적당한 모델의 field로 다른 모델을 ManyToManyField로 작성하면 M : N 관계를 구현할 수 있다.
- 이때, related_name을 설정해두는 것이 좋으며, field가 지정되지 않은 모델에서 field가 지정된 모델을 참조할 때 사용된다.
- 모델 변경
- accounts/models.py
class User(AbstractUser):
bookmarks = models.ManyToManyField("articles.Article", related_name="bookmark_users")
- migration 진행
- 중계테이블 자동 생성
Create
- accounts/urls.py
urlpatterns = [
path("register/", views.register),
path("me/", views.me),
path("articles/", views.my_articles),
path("bookmarks/<int:article_id>/", views.bookmark_article),
]
- accounts/views.py
- M : N 관계에서는 새로운 record를 직접 생성하는 방식이 아니라 ManyToManyField 로 지정된 field에 추가하는 방식으로 구현된다.
- 해당 field는 objects 와 같은 manager의 역할을 한다.
from articles.models import Article
@api_view(["POST"])
def bookmark_article(request, article_id):
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.add(article)
return Response(status=204)
Read
- User의 입장에서 bookmark한 Article 확인
- accounts/urls.py
urlpatterns = [
path("register/", views.register),
path("me/", views.me),
path("articles/", views.my_articles),
path("bookmarks/<int:article_id>/", views.bookmark_article),
]
- accounts/views.py
- ManyToManyField 로 지정된 field는 지정한 모델에 대해 objects 와 같은 manager의 역할을 하므로, 아래와 같이 사용 가능하다.
@api_view(["GET"])
def bookmarked_articles(request):
user = request.user
articles = user.bookmarks.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data, status=200)
- Article의 입장에서 bookmark한 User 확인
- articles/urls.py
urlpatterns = [
path("", views.article_list),
path("<int:pk>/", views.article_detail),
path("<int:pk>/comments/", views.comment_list),
path("<int:article_pk>/comments/<int:comment_pk>/", views.comment_detail),
path("<int:article_pk>/bookmarked_user/", views.bookmarked_user_list),
]
- articles/views.py
- ManyToManyField 로 지정된 모델은 지정될 때 설정한 related_name을 manager로 사용 가능하다.
from accounts.serializers import UserSerializer
@api_view(["GET"])
def bookmarked_user_list(request, article_pk):
article = Article.objects.get(pk=article_pk)
users = article.bookmarked_users.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
Delete
- Create와 비슷한 방식으로 진행한다.
- accounts/views.py
@api_view(["POST", "DELETE"])
def bookmark_article(request, article_id):
if request.method == "POST":
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.add(article)
elif request.method == "DELETE":
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.remove(article)
return Response(status=204)
로깅
- 로깅(logging)은 애플리케이션의 실행 과정에서 발생하는 이벤트를 기록하는 과정을 말한다.
- 프로그램의 실행 상태를 이해하고, 문제가 발생했을 때 원인을 분석하는 데 도움을 준다.
- 소프트웨어의 디버깅, 성능 모니터링, 보안 감사, 그리고 사용자의 행동 분석 등 다양한 목적으로 사용된다.
- mysite/settings.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": "general.log",
"formatter": "verbose",
},
},
"loggers": {
"": {
"level": "DEBUG",
"handlers": ["file"],
},
},
"formatters": {
"verbose": {
"format": "{name} {levelname} {asctime} {module} {message}",
"style": "{",
},
},
}
- articles/views.py
import logging
logger = logging.getLogger(__name__)
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
logger.info("articles")
return Response(serializer.data)
'Django' 카테고리의 다른 글
Access Token/Refresh Token (0) | 2024.03.21 |
---|---|
마이그레이션 : 관련 모델이나 필드가 해석되지 못할 때 (0) | 2024.03.19 |
Django : backend framework(5) (0) | 2024.03.19 |
Django : backend framework(4) (0) | 2024.03.18 |
Django : backend framework(3) (0) | 2024.03.15 |
M:N 관계
- M:N 관계(many to many relationship)는 한 테이블(A)의 한 행이 다른 테이블(B)의 여러 행과 관련이 있고, 반대로 한 테이블(B)의 한 행이 다른 테이블(A)의 여러 행과 관련이 있는 경우를 의미한다.
- 아래 예시에서
- 고객 테이블의 고객ID가 1인 행이 주문 테이블의 주문 ID가 101, 103인 행과 관련이 있고, 따라서 상품ID가 P001, P003인 행과 관련이 있다.
- 상품 테이블의 상품ID가 P001인 행이 주문 테이블의 주문 ID가 101, 104인 행과 관련이 있고, 따라서 고객ID가 1, 3인 행과 관련이 있다.
- 즉 고객 : 상품 = 1 : N, 상품 : 고객 = 1 : N 의 관계를 만족하는 경우 M:N 관계라고 한다.
- 고객 테이블
고객ID | 고객이름 | |
1 | 조나희 | nahee2253@nate.com |
2 | 조나단 | nahee2253@naver.com |
- 주문 테이블
주문ID | 고객ID | 상품ID | 날짜 |
101 | 1 | P001 | 2024-01-08 |
102 | 2 | P002 | 2024-01-09 |
- 상품 테이블
주문ID | 고객ID |
P001 | 노트북 |
P002 | 태블릿 |
중계 테이블
- 중계 테이블(association table)은 M:N 관계에서 두 테이블 간의 관계를 표현하기 위해 사용된다.
- 중계 테이블에는 두 테이블의 기본 키(PK)가 외래 키(FK)로서 포함된다.
- 위 예시의 주문 테이블 에는 고객 테이블과 상품 테이블의 PK가 FK로서 포함되고, 날짜 데이터가 추가되어 있다.
교사 - 학생의 관계에서
- 교사 - 학생은 어떤 관계를 가지고 있을까?
- 담임 교사의 관점
- 한 담임 교사는 여러 학생과 관계가 있다.
- 1 : N
- 한 학생은 한명의 담임 교사와 관계가 있다.
- 1 : 1
- → 담임 교사 - 학생의 관계는 1 : N 관계이다.
- 한 담임 교사는 여러 학생과 관계가 있다.
- 단순 교사의 관점
- 한 교사는 여러 학생과 관계가 있다.
- 1 : N
- 한 학생은 여러 교사와 관계가 있다.
- 1 : N
- → 교사 - 학생의 관계는 M : N 관계이다.
- 한 교사는 여러 학생과 관계가 있다.
- 담임 교사의 관점
- 추상화를 어떻게 하느냐에 따라, 즉 우리가 원하는 product에서 원하는 것이 무엇인지에 따라 사용하는 모델도 달라지게 된다.
- 즉, 기획은 구체적이어야 한다.]
[실습]
- 이전 실습에서 User - Article model간의 관계는 한명의 작성자(author)가 여러개의 Article을 작성할 수 있는 구조로, 1 : N 관계였다.
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
- User - Article은 해당 관계밖에 존재하지 않을까?
- User가 마음에 드는 Article들을 저장해두는 북마크 기능에 대해서
- 한명의 User가 여러 Article들을 북마크 할 수 있고
- 하나의 Article이 여러명의 User에게 북마크 될 수 있다
- User가 마음에 드는 Article들을 저장해두는 북마크 기능에 대해서
- M : N 관계에서 두개의 모델 중 적당한 모델의 field로 다른 모델을 ManyToManyField로 작성하면 M : N 관계를 구현할 수 있다.
- 이때, related_name을 설정해두는 것이 좋으며, field가 지정되지 않은 모델에서 field가 지정된 모델을 참조할 때 사용된다.
- 모델 변경
- accounts/models.py
class User(AbstractUser):
bookmarks = models.ManyToManyField("articles.Article", related_name="bookmark_users")
- migration 진행
- 중계테이블 자동 생성
Create
- accounts/urls.py
urlpatterns = [
path("register/", views.register),
path("me/", views.me),
path("articles/", views.my_articles),
path("bookmarks/<int:article_id>/", views.bookmark_article),
]
- accounts/views.py
- M : N 관계에서는 새로운 record를 직접 생성하는 방식이 아니라 ManyToManyField 로 지정된 field에 추가하는 방식으로 구현된다.
- 해당 field는 objects 와 같은 manager의 역할을 한다.
from articles.models import Article
@api_view(["POST"])
def bookmark_article(request, article_id):
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.add(article)
return Response(status=204)
Read
- User의 입장에서 bookmark한 Article 확인
- accounts/urls.py
urlpatterns = [
path("register/", views.register),
path("me/", views.me),
path("articles/", views.my_articles),
path("bookmarks/<int:article_id>/", views.bookmark_article),
]
- accounts/views.py
- ManyToManyField 로 지정된 field는 지정한 모델에 대해 objects 와 같은 manager의 역할을 하므로, 아래와 같이 사용 가능하다.
@api_view(["GET"])
def bookmarked_articles(request):
user = request.user
articles = user.bookmarks.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data, status=200)
- Article의 입장에서 bookmark한 User 확인
- articles/urls.py
urlpatterns = [
path("", views.article_list),
path("<int:pk>/", views.article_detail),
path("<int:pk>/comments/", views.comment_list),
path("<int:article_pk>/comments/<int:comment_pk>/", views.comment_detail),
path("<int:article_pk>/bookmarked_user/", views.bookmarked_user_list),
]
- articles/views.py
- ManyToManyField 로 지정된 모델은 지정될 때 설정한 related_name을 manager로 사용 가능하다.
from accounts.serializers import UserSerializer
@api_view(["GET"])
def bookmarked_user_list(request, article_pk):
article = Article.objects.get(pk=article_pk)
users = article.bookmarked_users.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
Delete
- Create와 비슷한 방식으로 진행한다.
- accounts/views.py
@api_view(["POST", "DELETE"])
def bookmark_article(request, article_id):
if request.method == "POST":
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.add(article)
elif request.method == "DELETE":
user = request.user
article = Article.objects.get(id=article_id)
user.bookmarks.remove(article)
return Response(status=204)
로깅
- 로깅(logging)은 애플리케이션의 실행 과정에서 발생하는 이벤트를 기록하는 과정을 말한다.
- 프로그램의 실행 상태를 이해하고, 문제가 발생했을 때 원인을 분석하는 데 도움을 준다.
- 소프트웨어의 디버깅, 성능 모니터링, 보안 감사, 그리고 사용자의 행동 분석 등 다양한 목적으로 사용된다.
- mysite/settings.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": "general.log",
"formatter": "verbose",
},
},
"loggers": {
"": {
"level": "DEBUG",
"handlers": ["file"],
},
},
"formatters": {
"verbose": {
"format": "{name} {levelname} {asctime} {module} {message}",
"style": "{",
},
},
}
- articles/views.py
import logging
logger = logging.getLogger(__name__)
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
logger.info("articles")
return Response(serializer.data)
'Django' 카테고리의 다른 글
Access Token/Refresh Token (0) | 2024.03.21 |
---|---|
마이그레이션 : 관련 모델이나 필드가 해석되지 못할 때 (0) | 2024.03.19 |
Django : backend framework(5) (0) | 2024.03.19 |
Django : backend framework(4) (0) | 2024.03.18 |
Django : backend framework(3) (0) | 2024.03.15 |