TIL(40일차)

김규현·2022년 10월 29일
0

📝오늘 배운 것

📌ModelSerializer

class ArticleListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ('id','title','image','created_at','user','likes','comments')

Model을 기반으로 만든 Serializer의 구조이다 만약 ModelSerializer를 사용하지 않는다면 models.py에서 모델을 만드는 것과 같이 번거롭게 필드를 하나하나 만들어주어야 한다.
하지만 ModelSerializer를 사용하면 선택한 model을 기반으로 필드를 자동으로 채워준다.

class ArticleListSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    likes = serializers.SerializerMethodField()
    comments = serializers.SerializerMethodField()

    def get_comments(self, obj):
        return obj.comments.count()

    def get_likes(self, obj):
        return obj.likes.count()

    def get_user(self, obj):
        return obj.user.email

    class Meta:
        model = Article
        fields = ('id','title','image','created_at','user','likes','comments')

만약 필드를 추가하고 싶다면 SerializerMethodField를 사용해서 위와 같이 만들고 싶은 필드를 만들어 ModelSerializer의 fields에 추가해주면 된다.

ForeginKey로 연결된 필드가 있을 경우, 기본값으로 ForeignKey로 연결된 필드의 pk를 가져오고 만약 pk가 아닌 다른 값을 가져오고 싶다면 위 코드에서 get_name으로 가져올 값을 다시 정의할 수 있다.

📌StringRelatedField

# models.py
class Article(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    content = models.TextField()
    image = models.ImageField(blank=True, upload_to='%Y/%m/%d/')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    likes = models.ManyToManyField(User, related_name='like_articles')
    
    def __str__(self):
        return str(self.title)
       
#user.models.py        
class User(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
    )
    def __str__(self):
        return self.email

#serializers.py
class ArticleSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    comments = CommentSerializer(many=True)
    likes = serializers.StringRelatedField(many=True)
    
    def get_user(self, obj):
        return obj.user.email
        
    class Meta:
        model = Article
        fields = '__all__'

위의 serializer는 Article 모델을 기반으로 만든 Modelserializer이고, Article 테이블에서 likes는
User 모델과 ManyToMany 관계이다.
위에서 설명한 ForeginKey와 마찬가지로 ManyTOMany 필드도 기본값으로 pk를 출력한다.
만약 pk가 아닌 string 값을 가져오고 싶다면 StringRelatedField를 사용해서 User 모델의 __str__ 메소드에서 정의한 string을 return하여 pk가 아닌 string 데이터를 출력할 수 있다.

위의 코드에서는 likes가 User를 나타내며 StringRelatedField를 사용하지 않았다면 유저의 pk 값이 나타날 것이고, StringRelatedField를 적용해줌으로서 User의 email 값 을 출력한다.

StringRelatedField의 인자로 many=True, read_only=True 등을 가질 수 있다.

📌symmetrical

ManyToManyField는 기본적으로 서로 대칭이 되는 관계이다.
팔로우 필드를 구현할 때 필드에 "self"를 추가하여 자기 자신을 참조하여 필드를 생성하게 되면
기본적으로 symmetrical=True로 설정되어 내가 상대를 팔로우 하면 상대도 나를 팔로우 하는 구조로 대칭이 되는 구조이다.
만약 내가 상대를 팔로우 하더라도 상대가 나를 팔로우 하지 않았을 때 팔로우가 되지 않는 구조로 만들려면 symmetrical=False를 필드에 추가하여 비대칭 구조로 만들어야 한다.

ForeignKey로 특정 테이블을 참조했을때 참조한 필드에서 해당 테이블에 접근을 할 수 있다.
하지만 반대로 참조를 당한(역참조) 테이블에서는 참조한 테이블에 접근할 수 없다.
이유는 참조를 한 테이블의 필드에는 해당 테이블이 있지만, 참조를 당한(역참조) 테이블에는 해당 필드가 없기 때문이다.

따라서 역참조 관계에 있는 필드에 접근하기 위해서는 related_name을 사용해서 접근해야한다.
만약 related_name을 설정하지 않고 접근할 때는 [classname]_set으로 역참조의 접근이 가능하며, related_name을 설정하면 [classname]_set이 아닌 설정한 name으로 접근을 하면 된다.

class Photo(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(max_length=1000, blank=True, null=True)


class Album(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(max_length=1000, blank=True, null=True)
    photo = models.ForeignKey(Photo, null=True, related_name='photos')
    
self.photo_a = Photo.objects.create(title='test photo')
self.Album_a = Album.objects.create(title='test Album', photo=self.photo_a)

예를 들어 Photo 테이블과 Photo 테이블을 참조한 Album 테이블이 있고 photo_a 객체와 Album_a 라는 객체를 생성했을 때 Album_a는 photo=self.photo_a.title로 Photo 테이블의 객체에 접근하여 test photo라는 값을 가져올 수 있다.

하지만 Photo _a 객체에서는 album 필드가 없기 때문에 위와 같이 가져올 수 없고, classname_set을 사용하여 self.photo_a.Album_set.all()로 역참조한 테이블에 접근하거나 related_name을 설정해서 self.photo_a.photos.all()으로 역참조한 테이블에 접근할 수 있다.

profile
웹개발 회고록

0개의 댓글