1:N 관계
- 1:N 관계(one to many relationship)은 한 테이블의 한 행이 다른 테이블의 여러 행과 관련이 있는 경우를 의미한다.
- 아래 예시에서, 고객 테이블의 고객ID가 1인 행이 주문 테이블의 주문 ID가 101, 103인 행과 관련이 있다.
- 고객 테이블
고객ID | 고객이름 | |
1 | 조나희 | nahee2253@nate.com |
2 | 조나단 | nahee2253@naver.com |
- 주문 테이블
주문ID | 고객ID | 상품명 | 날짜 |
101 | 1 | 노트북 | 2024-01-08 |
102 | 2 | 핸드폰 | 2024-01-09 |
- 이때, 고객ID 는 고객 테이블의 PK(Primary Key)이고, 주문 테이블의 FK(Foreign Key)이다.
Article - Comment
- 하나의 게시글에는 여러 개의 댓글이 존재할 수 있다.
- 1 : N 관계에서 1에 해당하는 모델의 field로 N에 해당하는 모델을 ForeignKey로 작성하면 1:N 관계를 구현할 수 있다.
- 모델 생성
- articles/models.py
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
content = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.id}: {self.content}"
- migration 진행
- 시리얼라이저 작성
- articles/serializers.py
from .models import Article, Comment
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "article", "content")
Create
- 주어진 스키마에 맞는 데이터를 입력받아(client) → 어떤 게시글에 대한 댓글을 작성할껀지 지정해줘(pk) → 그 데이터를 저장해줘(POST) → 응답해줘
- articles/urls.py
urlpatterns = [
path("", views.article_list),
path("<int:pk>/", views.article_detail),
path("<int:pk>/comments/", views.comment_list),
]
- articles/views.py
@api_view(["POST"])
def comment_list(request, pk):
data = request.data
serializer = CommentSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=201)
- request / response
- comment와 연관된 article을 넣어줘야한다.
- 시리얼라이저 변경
- articles/serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "article", "content")
extra_kwargs = {"article": {"read_only": True}}
- read_only 옵션을 넣는 이유는 validation check를 하지 않도록 하기 위해
- view 함수 변경
- articles/views.py
@api_view(["POST"])
def comment_list(request, pk):
article = Article.objects.get(pk=pk)
data = request.data
serializer = CommentSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save(article=article)
return Response(serializer.data, status=201)
- request / response

Read
- Comment에 대한 read는 Article을 통해서 하는 것이 자연스럽다
- 특정 Article을 Comment와 함께 줘(GET)
- 시리얼라이저 수정
- articles/serializer.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ("id", "title", "content", "comment_set")
- request / response

- 시리얼라이저 수정
- articles/serializer.py
- 기존에 사용하던 CommentSerializer를 사용하여 표현
- articles/serializer.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "article", "content")
extra_kwargs = {"article": {"read_only": True}}
class ArticleSerializer(serializers.ModelSerializer):
comment_set = CommentSerializer(many=True, required=False)
class Meta:
model = Article
fields = ("id", "title", "content", "comment_set")
- many=True : 하나의 article에 대해 comment가 여러 개일 수 있으므로 추가하는 옵션
- required=False : ArticleSerializer는 조회 뿐 아니라 생성에도 사용하므로, 생성할 시 comment를 필요로 하지 않는다는 옵션
- request / response

- id가 1인 article 조회 → comment의 정보에 article에 대한 정보가 포함되어 있음 → 불편
- 시리얼라이저 수정
- articles/serializer.py
- 시리얼라이저 수정
class ArticleSerializer(serializers.ModelSerializer):
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "content")
comment_set = CommentSerializer(many=True, required=False)
class Meta:
model = Article
# fields = "__all__"
fields = ("id", "title", "content", "comment_set")
- request / response

Update
- 주어진 스키마에 맞는 데이터를 입력받아(client) → 어떤 데이터를 바꿀껀지 지정해줘(pk) → 그 데이터를 수정해줘(PUT) → 응답해줘
- articles/urls.py
- pk가 2번 사용되기 때문에 어떤 model에 대한 pk인지 명시한다.
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),
]
- articles/views.py
@api_view(["PUT")
def comment_detail(request, article_pk, comment_pk):
if request.method == "PUT":
comment = Comment.objects.get(pk=comment_pk)
serializer = CommentSerializer(comment, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
- request / response

Delete
- 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),
]
- articles/views.py
@api_view(["PUT", "DELETE"])
def comment_detail(request, article_pk, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
if request.method == "PUT":
serializer = CommentSerializer(comment, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
elif request.method == "DELETE":
comment.delete()
return Response(status=204)
User - Article
- 하나의 유저는 여러 개의 게시글을 작성할 수 있다.
- 모델 변경
- articles/models.py
from django.conf import settings
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)
def __str__(self):
return f"{self.id}: {self.title}"
- migrations 진행
- 시리얼라이저 변경
- articles/serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "content")
comment_set = CommentSerializer(many=True, required=False)
class Meta:
model = Article
# fields = "__all__"
fields = ("id", "title", "content", "comment_set", "author")
extra_kwargs = {"author": {"read_only": True}}
Create
- view 변경
- articles/views.py
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
# return Response(serializer.data)
return Response(serializer.data)
elif request.method == "POST":
user = request.user
data = request.data
serializer = ArticleSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save(author=user)
return Response(serializer.data, status=201)
- request / response
- user가 누구인지 명시해야 하므로, requset할 때 JWT를 함께 넘겨줌
- request / response

Read
- user가 작성한 모든 게시글 조회
- accounts/urls.py
urlpatterns = [
path("register/", views.register),
path("me/", views.me),
path("articles/", views.my_articles),
]
- articles/views.py
from articles.serializers import ArticleSerializer
@api_view(["GET"])
def my_articles(request):
user = request.user
articles = user.article_set.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data, status=200)
- 1:N 관계에서 1(user)가 N(article)에 대한 데이터를 가져올 때 objects 매니저 대신 model_set 매니저를 대신 사용한다.
- request / response

'Django' 카테고리의 다른 글
Django : backend framework(6) (0) | 2024.03.19 |
---|---|
마이그레이션 : 관련 모델이나 필드가 해석되지 못할 때 (0) | 2024.03.19 |
Django : backend framework(4) (0) | 2024.03.18 |
Django : backend framework(3) (0) | 2024.03.15 |
Django : backend framework(2) (0) | 2024.03.14 |