DRF 난중일기

이번 글에선 DRF를 사용하며 겪었던 여러 오류들을 정리해보려 한다.
당신은 나처럼 삽질하지 말자

1. Nested serializers의 required=False 설정

보통 DRF에서 모델을 작성한 후, required를 설정하면 다음과 같이하게 된다.

models.py

class Mentoring(AbstractTimeStampModel):
    ...
    thumbnail=models.ImageField(null=True, blank=True)

serializers.py

extra_kwargs = {
    'thumbnail': {'required': False}
}

하지만 (외부 앱)Nested serializers 를 적용하면 얘기가 달라진다.
꼭 다음과 같이 extra_kwargs에 넣지 말고 따로 작성해서 삽질하는 일이 없도록 하자....

models.py

class Mentoring(AbstractTimeStampModel):
    ...
    tags=models.ManyToManyField(Tag)

serializers.py

class MentoringSerializer(serializers.HyperlinkedModelSerializer):
    ...
    tags = TagSerializer(required=False)

2. ~ with this name already exists.

serializers.py(mentoring)

class MentoringSerializer(serializers.HyperlinkedModelSerializer):
    tags = TagSerializer(many=True)
    ...
    def create(self, validated_data):
        tags_data=validated_data.pop('tags')
        ...
        for tag_data in tags_data:
            tag, created= Tag.objects.get_or_create(**tag_data)
            mentoring.tags.add(tag)
        return mentoring

models.py(tag)

class Tag(AbstractTimeStampModel):
    name=models.CharField(max_length=16, unique=True)

위의 코드는 metoring 모델의 serializers에서 tag라는 nested serializers 를 불러와 외래키로써 사용하는 예시이다. 여기서 만약 tag의 name이 원래 있던 것과 중복된다면, tag, created= Tag.objects.get_or_create(**tag_data) 를 쓰기도 전에 def create(self, validated_data): 에서 막혀버린다.

HTTP/1.1 400 Bad Request
Date: Sun, 03 Oct 2021 07:06:07 GMT
Server: WSGIServer/0.2 CPython/3.9.6
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
X-Frame-Options: DENY
Content-Length: 58
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
{
  "tags": [
    {
      "name": [
        "tag with this name already exists."
      ]
    }
  ]
}

이를 해결하기 위해서는 tag의 validated_data 과정에서 예외를 처리해주면 된다.

serializers.py(tag)

class TagSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Tag
        fields = '__all__'
        read_only_fields = []
        extra_kwargs = {
            'name': {'validators': []},
        }

3. 슬래시'/' 와 301 Moved Permanently

장고에서 라우트 설정을 하다 보면 '/'를 넣을지 말지 고민하는 경우가 있다. 각 경우에 대해 나타나는 현상을 정리하면 다음과 같다.

(1) '/'를 빼는 경우

from django.urls import path, include
from .views import  LogoutAPI, RegistrationAPI, LoginAPI, UserAPI
urlpatterns =[
    path("register", RegistrationAPI.as_view()),
    path("login", LoginAPI.as_view()),
    path("user", UserAPI.as_view()),
    path("logout", LogoutAPI.as_view()),
]

auth/register 로 접근 시 -> 정상 접속
auth/register/ 로 접근 시 -> 404 에러

(2) '/'를 넣는 경우

from django.urls import path, include
from .views import  LogoutAPI, RegistrationAPI, LoginAPI, UserAPI
urlpatterns =[
    path("register", RegistrationAPI.as_view()),
    path("login", LoginAPI.as_view()),
    path("user", UserAPI.as_view()),
    path("logout", LogoutAPI.as_view()),
]

auth/register 로 접근 시 -> 301 에러
auth/register/ 로 접근 시 -> 정상 접속

(2)에서 301 Moved Permanently 에러가 나는 이유는 장고가 자체적으로 '/' 가 없는 주소를 '/' 가 있는 주소로 리다이렉트 하면서 발생하는 것이다.

결론적으로, '/'를 붙이는 것은 하나로 통일하여 미연에 에러를 방지하는 것이 좋다.

좋은 웹페이지 즐겨찾기