์ƒˆ์†Œ์‹

ISSUE/Django

[ISSUE] Tag ํฌํ•จ๋œ Site ๋ชจ๋ธ์˜ serialize ์ฒ˜๋ฆฌ

  • -

[22.11.27] 

๐Ÿƒ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ํ™”๋ฉด

1. ์ด์ „ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ์‚ฌ์ดํŠธ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ํ•ด๋‹น ์‚ฌ์ดํŠธ์— Tag๋ฅผ Many-to-Many๊ด€๊ณ„๋กœ ๋“ฑ๋กํ•˜๋Š” ์ง„ํ–‰

2. ์ด์ œ ํƒœ๊ทธ๊ฐ€ ๋“ฑ๋ก๋œ ์‚ฌ์ดํŠธ๋ฅผ ์ตœ์‹ ์ˆœ์œผ๋กœ 4~6๊ฐœ ์กฐํšŒ ํ›„ ๋“ฑ๋ก๋œ ๋ชจ๋“  ํƒœ๊ทธ๋ฅผ ์กฐํšŒํ•˜์—ฌ ํ•˜๋‹จ์— Tag buttonํ˜•ํƒœ๋กœ ๋™์ ์œผ๋กœ ๋ฟŒ๋ ค์ฃผ๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•  ๊ฒƒ์ด๋‹ค.

3. ์œ„์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ Siteํ•ญ๋ชฉ์„ ์กฐํšŒํ•  ๋ฟ ์•„๋‹ˆ๋ผ Many-to-Many๊ด€๊ณ„๋กœ ๋“ฑ๋ก๋œ ํƒœ๊ทธ๋„ ์กฐํšŒ๋ฅผ ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— Serializer ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

  • ๋ฌผ๋ก  api 2๊ฐœ๋ฅผ ์š”์ฒญํ•˜์—ฌ ๊ฐ๊ฐ ์‚ฌ์ดํŠธ ๋ฐ์ดํ„ฐ, ํƒœ๊ทธ ๋ฐ์ดํ„ฐ ์กฐํšŒํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ 2๋ฒˆ์„ ์š”์ฒญํ•œ๋‹ค๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋น„ํšจ์œจ์ ์ด๋ฉฐ drf์— ์กด์žฌํ•˜๋Š” serialize๋ฅผ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค๊ณ  ํŒ๋‹จ

1. views.py > TagsAPIView ๊ตฌํ˜„

class TagsAPIView(APIView):
    """
     Site model์— Tag model๊ฐ’์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ๋งŒ ์กฐํšŒ
    """

    def get(self, request):
        word: str  = request.GET['word']

        tags = [tag for tag in Tag.objects.all()]

        list_qs = Site.objects.filter(
                            (Q(tag__in=tags))&(
                            Q(title__contains=word)|
                            Q(host_name__contains=word))).distinct()


        serializer = SiteSerializer(list_qs, many=True)
        return Response(serializer.data)
  • /api/tags?word= api์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” view ๋‹จ ์ž‘์—… ์ฝ”๋“œ์ด๋‹ค.
  • word๋Š” ๊ฒ€์ƒ‰์œผ๋กœ ์กฐํšŒ ์‹œ ์š”์ฒญ๋˜๋Š” ๊ฐ’์ด๋ฏ€๋กœ ์ž์„ธํ•œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.
  • ๋จผ์ € ํƒœ๊ทธ๊ฐ€ ๋“ฑ๋ก๋œ ์‚ฌ์ดํŠธ ํ•ญ๋ชฉ์„ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋“  ํƒœ๊ทธ๋ฅผ ์กฐํšŒํ•˜์—ฌ tags์— ํ• ๋‹น
  • Site๋ชจ๋ธ์— tag๊ฐ€ ์œ„์—์„œ ํ• ๋‹น๋ฐ›์€ tags๊ฐ€ ํฌํ•จ๋˜๋ฉฐ word๋กœ ๋„˜์–ด์˜จ ๊ฐ’์ด ํฌํ•จํ•œ ๊ฐ’์ด ์กฐํšŒ ๋˜๊ฒŒ filter๋ฅผ ์ž‘์„ฑ
  • ๋‹จ, ์ค‘๋ณต๋œ ๊ฐ’์ด ์กฐํšŒ๋˜์ง€ ์•Š๊ฒŒ distinct()๋ฅผ ์‚ฌ์šฉ
  • serializer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ ์…‹ ๋ฐ ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค์™€ ๊ฐ™์€ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ๋ฅผ Python ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ JSON, XML ๋˜๋Š” ๋‹ค๋ฅธ ์ฝ˜ํ…์ธ  ์œ ํ˜•์œผ๋กœ ์‰ฝ๊ฒŒ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— queryset์„ SiteSerializer์— ๋„ฃ์–ด ๋ฐ˜ํ™˜๋ฐ›์•„ ๋ฆฌํ„ด ์‹œ์ผฐ๋‹ค.

๐Ÿ“Œ SiteSerializer

lass SiteSerializer(ModelSerializer):

    CATEGORY_CHOICES = [(1, 'python'), (2, 'django'), (3, 'javascript'), (4, 'orm'), (5, 'mysql'), (6, 'drf'), (7, 'docker'), (8, 'os'), (9, 'aws'), (10, 'html'), (11, 'css'), (12, 'git'), (13, 'linux')]

    title           = serializers.CharField(max_length=100, allow_blank=False, trim_whitespace=True)
    thumbnail_url   = serializers.URLField(max_length=200, min_length=None, allow_blank=False)
    host_name       = serializers.CharField(max_length=30, allow_blank=False, trim_whitespace=True)
    content         = serializers.CharField(max_length=2000, allow_blank=True)
    category        = serializers.ChoiceField(choices=CATEGORY_CHOICES)
    user            = serializers.CharField(max_length=10, allow_blank=False, trim_whitespace=True)
    favorite        = serializers.BooleanField(default=False)
    video           = serializers.BooleanField(default=False)

    def create(self, validated_data):
        return Site.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.category = validated_data.get('category', instance.category)
        instance.favorite = validated_data.get('favorite', instance.favorite)
        instance.video = validated_data.get('video', instance.video)
        instance.save()
        return instance    

    class Meta:
        model = Site
        exclude = ["created_at", "updated_at"]
  • ํ˜„์žฌ ์‚ฌ์ดํŠธ ์‹œ๋ฆฌ์–ผ๋ผ์ด์ฆˆ๋Š” ์œ„์™€ ๊ฐ™์ด Site๋ชจ๋ธ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” field๋ฅผ ์ƒ๋‹จ์— ์„ ์–ธ
  • create์™€ update ์ž‘์—…์ด ์ด๋ฃจ์–ด์งˆ ๋•Œ validation์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ์ˆ˜ ์ž‘์„ฑ
  • ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์€ created_at์™€ updated_at๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๊ฐ’์ด ์กฐํšŒ๋˜๊ฒŒ exclude์— created_at, updated_at ์„ ํ• ๋‹นํ•ด ์ฃผ์—ˆ๋‹ค.

2. ๊ธฐ์กด serializer๋ฅผ ์ด์šฉํ•œ response

โœ”๏ธ [http://127.0.0.1:8000/api/tags?word=](http://127.0.0.1:8000/api/tags?word=) api๋ฅผ ํ˜ธ์ถœ, ํ˜„์žฌ response๊ฐ’


โœ”๏ธ ํ˜„์žฌ response๊ฐ’์„ ์ด์šฉํ•ด์„  ์‚ฌ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํƒœ๊ทธ ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š์•„ ์œ„์˜ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ ํ•˜๋‹จ์— ํƒœ๊ทธ๋ฅผ buttonํ˜•์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†๋‹ค.

โœ”๏ธ serializer๋Š” ํ•œ ๋ฒˆ๋งŒ ๊ฑธ์ณ์„œ tag๊ฐ’์ด ํฌํ•จ๋œ site๊ฐ’์„ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด models.py์™€ serializers.py๋ฅผ ์ˆ˜์ •ํ•ด ์ฃผ๊ฒ ๋‹ค.

๐Ÿ“Œ serializers.py

โœ”๏ธ drf ์ •์‹ ๋ฌธ์„œ๋ฅผ ๋ณด์—ฌ ํ•˜๋‚˜์˜ ์‹œ๋ฆฌ์–ผ๋ผ์ด์ฆˆ์—์„œ many-to-many๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„ ๋‘ ๊ฐœ์˜ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

 

โœ”๏ธ Nested relationships ์ด๋ผ๊ณ  ํ•˜๋ฉฐ ์„œ๋กœ ๋‘ ๋ชจ๋ธ ๊ฐ„ ๊ด€๊ณ„๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

class SiteSerializer(ModelSerializer):

    CATEGORY_CHOICES = [(1, 'python'), (2, 'django'), (3, 'javascript'), (4, 'orm'), (5, 'mysql'), (6, 'drf'), (7, 'docker'), (8, 'os'), (9, 'aws'), (10, 'html'), (11, 'css'), (12, 'git'), (13, 'linux')]

    title           = serializers.CharField(max_length=100, allow_blank=False, trim_whitespace=True)
    thumbnail_url   = serializers.URLField(max_length=200, min_length=None, allow_blank=False)
    host_name       = serializers.CharField(max_length=30, allow_blank=False, trim_whitespace=True)
    content         = serializers.CharField(max_length=2000, allow_blank=True)
    category        = serializers.ChoiceField(choices=CATEGORY_CHOICES)
    user            = serializers.CharField(max_length=10, allow_blank=False, trim_whitespace=True)
    favorite        = serializers.BooleanField(default=False)
    video           = serializers.BooleanField(default=False)

    **tag             = TagSerializer(many=True, read_only=True)**

    def create(self, validated_data):
        return Site.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.category = validated_data.get('category', instance.category)
        instance.favorite = validated_data.get('favorite', instance.favorite)
        instance.video = validated_data.get('video', instance.video)
        instance.save()
        return instance    

    class Meta:
        model = Site
                # exclude = ["created_at", "updated_at"]
        fields = ['title', 'thumbnail_url'
                                    , 'host_name', 'content'
                                    , 'category', 'user', 'favorite', 'video', **'tag'**]

1. ๋ฐ˜ํ™˜๋˜๋Š” fields์— tag๋ฅผ ํฌํ•จํ•˜์—ฌ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ์ถ”๊ฐ€

2. SiteSerializer๊ฐ€ tag๋ฅผ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ƒ๋‹จ์— tag ํ•„๋“œ ์„ค์ •

3. tagํ•„๋“œ๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด TagSerializer์ž‘์„ฑ

  • site์— ๋ฌถ์ธ ๋ชจ๋“  tag๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด many=True, read_only=True์†์„ฑ ์ถ”๊ฐ€
class TagSerializer(ModelSerializer):

    name = serializers.CharField(max_length=20, allow_blank=False, trim_whitespace=True)
    class Meta:
        model = Tag
        fields = ['id', 'name']

1. name ํ•„๋“œ ๊ฐ’์„ ์„ค์ •

2. ๋ฐ˜ํ™˜๋˜๋Š” field ์„ค์ •

  • ํƒœ๊ทธ ์ด๋ฆ„์€ ํ™”๋ฉด์— ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ์‚ฌ์šฉ
  • id ๊ฐ’์€ ํ•ด๋‹น ํƒœ๊ทธ ์„ ํƒ ์‹œ api ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‹๋ณ„์ž ๊ฐ’์œผ๋กœ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

โœ”๏ธ ์ด๋ ‡๊ฒŒ TagSerializer๋„ ์ƒ์„ฑ์„ ํ–ˆ๊ณ  SiteSerializer๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์— tag๊ฐ€ ํฌํ•จ๋˜๊ฒŒ ์ฝ”๋“œ์ž‘์„ฑ์„ ํ•ด ์ฃผ์—ˆ๋‹ค.

 

 

โœ”๏ธํ•˜์ง€๋งŒ ๊ฒฐ๊ณผ๋Š” ๋™์ผํ•˜๊ฒŒ tag๋Š” ํฌํ•จ๋˜์ง€ ์•Š๊ณ  response๊ฐ’์ด ๋‚˜์˜จ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

3. models.py related_name ์„ค์ •

class Tag(models.Model):
    """ ์›น ํ•ญ๋ชฉ ํƒœ๊ทธ ๋ชฉ๋ก ๋ชจ๋ธ  """
    name = models.CharField(verbose_name='์ด๋ฆ„', max_length=20)
    site = models.ManyToManyField(Site, **related_name='tag'**,verbose_name='๋ฆฌ์ŠคํŠธ')

    def __str__(self):
        return f"{self.name}"

    class Meta:
        verbose_name = 'ํƒœ๊ทธ'
        verbose_name_plural = 'ํƒœ๊ทธ ๋ชฉ๋ก'

โœ”๏ธ Tag ๋ชจ๋ธ์„ ํƒ์ง€ํ•  ๋•Œ site์—์„œ ๊ฐ’์„ ์ฝ์–ด ๋‚ผ ์ˆ˜ ์žˆ๋„๋ก related_name์„ tag๋กœ ์„ค์ • ํ›„ ๋‹ค์‹œ migrate ํ•˜๊ณ  ์ง„ํ–‰ํ•˜์—ฌ ๋ณด๊ฒ ๋‹ค.

4. ์ˆ˜์ •ํ•œ serializer๋ฅผ ์ด์šฉํ•œ response

 

๐Ÿƒ ์ฐธ๊ณ 

Django

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

Serializer relations

 

Serializer relations - Django REST framework

relations.py Data structures, not algorithms, are central to programming. — Rob Pike Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse re

www.django-rest-framework.org

 

 

Contents

ํฌ์ŠคํŒ… ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค

์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต๊ฐ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.