모델링을 설계하다보면 ManyToMany 필드처럼 클래스 모델을 추가로 생성하지 않고,
한 모델이 여러 개의 모데로가 관계를 맺어야 하는 경우가 있다. 예를 들어, 댓글이나 좋아요와 같은 기능을 구현할 때 주로 사용된다.
class Post(models.Model):
# 생략
class Comment(models.Model):
# 생략
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
다양한 모델링이 있지만, Like 를 PostLike 와 CommentLike 로 나눌 수 있고 방법은 다양하다.
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
class Post(models.Model):
# 생략
like = GenericRelation('Like', related_query_name='post')
class Comment(models.Model):
# 생략
like = GenericRelation('Like', related_query_name='comment')
class Like(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
# contenttypes과는 무관한 user
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
먼저, Like 모델부터 봅시다.
class Like(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
- content_type
- -> content_type 필드는 ContentType 모델과 ForeignKey로 연결되는 필드이다.
- -> content_type에는 'id', 'app_label', 'model' 인자가 들어가있다.
CREATE TABLE `django_content_type` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_label` varchar(100) NOT NULL,
`model` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `django_content_type_app_label_model_76bd3d3b_uniq` (`app_label`,`model`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
- objects_id
- 관련될 모델의 PK를 저장할 수 있는 필드입니다.
- content_object
- GenericForeignKey 에 위 두가지 필드를 전달한다.
Post 모델을 보겠습니다. (Comment 모델과 동일함으로 Post만 다루겠음)
class Post(models.Model):
# 생략
like = GenericRelation('Like', related_query_name='post')
related_query_name 를 통해서 관련 개체에서 조회를 할 수 있다.
post의 title이 '첫번째' 라는 키워드를 포함하고 있는 Like 객체들을 필터링합니다.
orm, sql example
아래는 contenttypes을 사용할 시 사용할 여러 orm과 그에 해당되는 sql에서 대해서 정리해보았습니다.
Like 객체 생성
user = User.objects.get(id=user_id)
post = Post.objects.get(id=post_id)
like = Like.objects.create(content_object=post, user=user)
SQL
INSERT INTO `post_like` (`content_type_id`, `object_id`, `user_id`, `created_at`, `updated_at`)
VALUES (9, 2, 2, '2020-08-20 18:20:02.885304', '2020-08-20 18:20:02.885373');
args=[9, 2, 2, b'2020-08-20 18:20:02.885304', b'2020-08-20 18:20:02.885373']
Like 객체 조회
post = Post.objects.get(id=post_id)
ct = ContentType.objects.get_for_model(post)
like = Like.objects.get(
content_type=ct,
object_id=post.id,
user=request.user
)
SQL
SELECT `post_like`.`id`, `post_like`.`content_type_id`, `post_like`.`object_id`, `post_like`.`user_id`, `post_like`.`created_at`, `post_like`.`updated_at`
FROM `post_like`
WHERE (`post_like`.`content_type_id` = 9 AND `post_like`.`object_id` = 2 AND `post_like`.`user_id` = 2);
args=(9, 2, 2)
특정 User의 특정 Post에 대한 Like 조회
post = Post.objects.get(id=post_id)
ct = ContentType.objects.get_for_model(post)
likes = Like.objects.filter(content_type=ct, user=request.user)
SQL
SELECT post_like.id, post_like.content_type_id, post_like.object_id, post_like.user_id, post_like.created_at, post_like.updated_at
FROM post_like
WHERE (post_like.content_type_id = 9 AND post_like.user_id = 2)
LIMIT 21;
args=(9, 2)
특정 Post에 대한 Like의 수 조회
post = Post.objects.get(id=post_id)
ct = ContentType.objects.get_for_model(post)
like_cnt = Like.objects.filter(content_type=ct, object_id=post.id).count()
SQL
SELECT COUNT(*) AS `__count`
FROM `post_like`
WHERE (`post_like`.`content_type_id` = 9 AND `post_like`.`object_id` = 2);
args=(9, 2)
'Django > DRF' 카테고리의 다른 글
[DRF] queryset과 get_queryset()의 차이점 (0) | 2022.05.26 |
---|---|
[Django] DecimalField (0) | 2022.05.25 |
[Django rest Framework] get_object() (0) | 2022.04.23 |
[Django rest Framework] Swagger 문서 자동화 ( drf-yasg ) (0) | 2022.04.22 |
[Django rest framework] TDD - Unit Test (0) | 2022.04.20 |