티스토리 뷰

bookmared_movies 에 원래는 movie id만 출력되었었다.

다음과 같이 해당하는 id의 영화데이터를 출력하려고 했는데 막막했다.

{
    "username": "test1",
    "followings_count": 1,
    "followings": [
        "test2"
    ],
    "followers_count": 1,
    "followers": [
        "test2"
    ],
    "bookmarked_movies": [
        {
            "id": 11,
            "title": "스타워즈: 에피소드 4 새로운 희망",
            "overview": "공화국이 붕괴하고 제국이 수립된 뒤 20년, 제다이 기사단은 전멸하고 강력한 제국군의 횡포에 은하계는 공포에 휩싸여 있다. 그러던 중 공화국 재건을 노리는 반란군이 제국군의 비밀병기 데스스타 설계도를 훔쳐 달아나고 제국군은 이를 쫓는다. 하지만 결국 제국의 손에 붙잡히게 된 그들은 드로이드 R2-D2에 설계도를 넣어서 R2의 친구 C-3PO와 탈출시키는 데 성공하고, 두 드로이드 콤비는 타투인의 시골 마을에서 숙부와 함께 살고 있던 청년 루크 스카이워커에게 오게 되는데...",
            "popularity": 56.059,
            "vote_count": 16279,
            "vote_average": 8.2,
            "release_date": "1997-04-12",
            "poster_path": "/7XFfURIFCJxN1mfBg0SAjk5yGzg.jpg",
            "ranking": 78,
            "year": "1997",
            "genre_ids": [
                12,
                28,
                878
            ],
            "bookmarked_users": [
                5
            ]
        }
    ],
    "id": 5
}

일단 다른 앱에서 모델을 불러오는게 안되는 줄 알았는데

../ .. 등 상대경로는 쓸수가 없고 

아래 코드처럼 앱 이름 .models 로 불러오면 된다!!

ProfileSerializer는 accounts App에 있고 Movie 는 movies App 에 있기 때문에 accounts 앱에서

 

from movies.model import Movie

 

하면 된다.

 

그리고 해당 영화정보를 출력하기 위해 새로운 MovieSerializer를 만든다.

그리고 bookmarked_movies에 그 시리얼라이저를 넣어주면 끝이다.

이렇게 간단하다고? 황당하다. 네스티드 해야되나, 필드를 추가해야되나, source 를 써야하나....계속 고민했는데 말이다.

내가 시리얼라이저를 완전히 이해하지 못한 것일까..ㅠㅠ

너무너무 어렵다..!

 

하나의 시리얼라이저안에 그 모델이 지닌 필드가 a라고 한다면

여기서는 User가 bookmarked_movies 라는 필드를 가지고 있는 것이기에 (MtoM으로 미리 연결하였기 때문에)

bookmarked_movies = Movieserializer(many=True) 만 해줘도

bookamarked_movies 안에 북마크된 영화 정보만 쏙!! 나오게 된다.

 

from movies.models import Movie

# 프로필 정보
# StringRelatedField
class MovieSerializer(serializers.ModelSerializer):
    class Meta:
        model = Movie
        fields = '__all__'

class ProfileSerializer(serializers.ModelSerializer):
    bookmarked_movies = MovieSerializer(many=True)
    followings = serializers.StringRelatedField(many=True)
    followers = serializers.StringRelatedField(many=True)
    followings_count = serializers.IntegerField(source='followings.count', read_only=True) 
    followers_count = serializers.IntegerField(source='followers.count', read_only=True)

    class Meta:
        model = get_user_model()
        fields = ('username', 'followings_count','followings','followers_count', 'followers','bookmarked_movies', 'id')

 

기존에 이미 (관계가 연결되어서) 필드가 존재하는 경우에는

바로 시리얼라이저를 넣으면 자동으로 연결이 되는 것 같고, 

그렇지 않고 새로운 필드를 만들되 해당 Model의 필드를 활용할 수 있는 경우는 source 를 쓰는 것 같다.

 

위 코드에서도 유저모델안에 followings와 followers라는 이름으로 user 모델이 self로 MtoM으로 연결되어 있는 상태이기 때문에

followings와 follewers를 바로 쓸 수 있는 것이고 , StringRelatedField를 쓰면 str(여기서는 username) 값이 나온다.

따라서 정확한 유저정보를 알고싶다면 Movie에서 했던 것처럼 UserSerializer 를 넣으면 유저정보가 모두 나올 것이다.

 

실험해보았다. 유저정보가 잘 나오고 있다.

class ProfileSerializer(serializers.ModelSerializer):
    bookmarked_movies = MovieSerializer(many=True)
    followings = UserSerializer(many=True)
    followers = UserSerializer(many=True)
    # followings = serializers.StringRelatedField(many=True)
    # followers = serializers.StringRelatedField(many=True)
    followings_count = serializers.IntegerField(source='followings.count', read_only=True) 
    followers_count = serializers.IntegerField(source='followers.count', read_only=True)

    class Meta:
        model = get_user_model()
        fields = ('username', 'followings_count','followings','followers_count', 'followers','bookmarked_movies', 'id')
{
    "username": "test1",
    "followings_count": 1,
    "followings": [
        {
            "username": "test2"
        }
    ],
    "followers_count": 1,
    "followers": [
        {
            "username": "test2"
        }
    ],
    "bookmarked_movies": [
        {
            "id": 11,
            "title": "스타워즈: 에피소드 4 새로운 희망",
            "overview": "공화국이 붕괴하고 제국이 수립된 뒤 20년, 제다이 기사단은 전멸하고 강력한 제국군의 횡포에 은하계는 공포에 휩싸여 있다. 그러던 중 공화국 재건을 노리는 반란군이 제국군의 비밀병기 데스스타 설계도를 훔쳐 달아나고 제국군은 이를 쫓는다. 하지만 결국 제국의 손에 붙잡히게 된 그들은 드로이드 R2-D2에 설계도를 넣어서 R2의 친구 C-3PO와 탈출시키는 데 성공하고, 두 드로이드 콤비는 타투인의 시골 마을에서 숙부와 함께 살고 있던 청년 루크 스카이워커에게 오게 되는데...",
            "popularity": 56.059,
            "vote_count": 16279,
            "vote_average": 8.2,
            "release_date": "1997-04-12",
            "poster_path": "/7XFfURIFCJxN1mfBg0SAjk5yGzg.jpg",
            "ranking": 78,
            "year": "1997",
            "genre_ids": [
                12,
                28,
                878
            ],
            "bookmarked_users": [
                5
            ]
        }
    ],
    "id": 5
}

한편 followings_count, followers_count 의 경우 user 모델이 가지고 있는 필드가 아니라 새롭게 정의하는 경우다.

이미 관계 연결이 되어있기 때문에 소스에서 가져와서 정수값으로 필드만 만들어준다고 생각하면 된다.!

 

 

 

추가적으로...

어떤때 Nested 를 쓰는지, 어떤때 SerializerMethodField() 를 쓰는지 .. 구글링을 100번은 해본 것 같다. 

Nested 는 말그대로 다른 시리얼라이저를 내부에서 불러와 쓰는 경우인 것이고 MethodField()는 json을 새롭게 재구성할때 쓴다. 이번 프로젝트에서는 MethodField를 사용하여 장르별 개봉순 영화와 연도별 평균별점순 영화를 재구성하였다. 

이때 기본이 되는 모델은 각각 장르와 연도였고, 각 장르마다, 연도마다 돌면서 그 안에 필터링을 돌린 영화 쿼리셋을 출력하는 형태였다.

아래 코드처럼 movies_by_year란느 새로운 필드를 정의내렸다.

이거 해결하는데 하루 넘게 쓴 것 같다..

결론은 MethodField()는 def 즉 함수를 통해서 한번 필터링을 거치거나 무언가를 처리하여 결과물을 반환하는 역할을 하는 것 같다.

movies_by_genre 안에 해당 장르의 영화만을 뽑아내기 위해 필터를 돌렸다. 이때 obj가 도대체 뭐지>>>>>?????하고 혼란스러웠는데 삽질 결과 obj는 genre 임을 알 수 있었다. 정확히 말하면 장르 쿼리셋(스포츠, 판타지, 로맨스......) 이 차례대로 돈다고 생각할 때, 하나의 (스포츠) 오브젝트가 obj 인 것이다!! 그러니까 처음 obj.id 는 첫 번째 오브젝트의 id 즉 스포츠라는 장르의 아이디가 되는 것이다. 

따라서 영화의 장르아이디(이미 MtoM으로 연결해놔서 필드를쓸수있음) == 스포츠의 장르아이디   인 애들을 뽑아서

values()까지 붙여서 return 시키면 뿅! 하고 해당 장르 영화만 출력된다.

class Movie(models.Model):
    title = CharField(max_length=100)
    overview = TextField()
    popularity = models.FloatField()
    vote_count = models.IntegerField()
    vote_average = models.FloatField()
    release_date = models.DateField()
    poster_path = models.CharField(max_length=200)
    ranking = models.IntegerField()
    # 1:N
    year = models.ForeignKey(Year, on_delete=models.CASCADE, blank=True)
    # M:N
    genre_ids = models.ManyToManyField(Genre, related_name='movie_genre')
    # 이 영화를 북마크 한 사람
    bookmarked_users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="bookmarked_movies")
# 장르별 개봉순 영화
class RecentMovieByGenreListSerializer(serializers.ModelSerializer):
    movies_by_genre = serializers.SerializerMethodField()

    class Meta:
        model = Genre
        fields = ('id', 'name', 'movies_by_genre',)

    def get_movies_by_genre(self, obj):
        movies = Movie.objects.all().order_by('-release_date')
        return movies.filter(genre_ids=obj.id).values() # 입력받은 아이디와 일치여부 확인,.values()를 붙여야
[
    {
        "id": "2021",
        "movies_by_year": [
            {
                "id": 635302,
                "title": "귀멸의 칼날 극장판 무한열차편",
                "overview": "혈귀로 변해버린 여동생 네즈코를 인간으로 되돌릴 단서를 찾아 비밀조직 귀살대에 들어간 탄지로. 젠이츠, 이노스케와 새로운 임무 수행을 위해 무한열차에 탑승 후 귀살대 최강 검사 염주 렌고쿠와 합류한다. 달리는 무한열차에서 승객들이 하나 둘 흔적 없이 사라지자 숨어있는 식인 혈귀의 존재를 직감하는 렌고쿠. 귀살대 탄지로 일행과 최강 검사 염주 렌고쿠는 어둠 속을 달리는 무한열차에서 모두의 목숨을 구하기 위해 예측불가능한 능력을 가진 혈귀와 목숨을 건 혈전을 시작하는데...",
                "popularity": 700.117,
                "vote_count": 1860,
                "vote_average": 8.4,
                "release_date": "2021-01-27",
                "poster_path": "/m2FNRngyJMyxLatBMJR8pbeG2v.jpg",
                "ranking": 26,
                "year_id": "2021"
            },
            {
                "id": 791373,
                "title": "잭 스나이더의 저스티스 리그",
                "overview": "슈퍼맨이 죽고 지구에 어둠의 그림자가 드리운다. 마더박스를 차지하기 위해 빌런 스테픈울프가 파라데몬 군단을 이끌고 지구에 온 것이다. 지구를 지키기 위해 목숨을 바친 슈퍼맨의 희생이 헛되지 않도록 하기 위해 브루스 웨인은 다이애나 프린스와 적에 맞서기로 한다. 배트맨과 원더 우먼은 새로이 등장한 위협에 맞서 싸우기 위해 특별한 능력을 가진 메타휴먼, 아쿠아맨과 사이보그, 플래시를 찾아가 설득하여 힘을 합친다. 드디어 한 팀이 된 저스티스 리그. 혹시 스테픈울프와 데사드 그리고 다크사이드를 물리치기에 너무 늦어버린 것이 아닐까?",
                "popularity": 369.347,
                "vote_count": 6852,
                "vote_average": 8.4,
                "release_date": "2021-03-18",
                "poster_path": "/qd7iPB26bwMPZqRcnflzAeDkMn.jpg",
                "ranking": 31,
                "year_id": "2021"
            },
            {
                "id": 776503,
                "title": "코다",
                "overview": "24/7 함께 시간을 보내며 소리를 들을 수 없는 가족을 세상과 연결하는 코다 '루비'는 짝사랑하는 '마일스'를 따라간 합창단에서 노래하는 기쁨과 숨겨진 재능을 알게 된다. 합창단 선생님의 도움으로 마일스와의 듀엣 콘서트와 버클리 음대 오디션의 기회까지 얻지만 자신 없이는 어려움을 겪게 될 가족과 노래를 향한 꿈 사이에서 루비는 망설이는데…",
                "popularity": 55.157,
                "vote_count": 277,
                "vote_average": 8.3,
                "release_date": "2021-08-31",
                "poster_path": "/hqu5qQqgm1OivfgXpX0eU6DPFXc.jpg",
                "ranking": 58,
                "year_id": "2021"
            },

 

댓글