이번 글에선 DRF를 사용하며 겪었던 여러 오류들을 정리해보려 한다.
당신은 나처럼 삽질하지 말자
보통 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)
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': []}, }
장고에서 라우트 설정을 하다 보면 '/'를 넣을지 말지 고민하는 경우가 있다. 각 경우에 대해 나타나는 현상을 정리하면 다음과 같다.
(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 에러가 나는 이유는 장고가 자체적으로 '/' 가 없는 주소를 '/' 가 있는 주소로 리다이렉트 하면서 발생하는 것이다.
결론적으로, '/'를 붙이는 것은 하나로 통일하여 미연에 에러를 방지하는 것이 좋다.