Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serializer에서 BooleanField의 값이 존재하는지 validation을 하지 않는 것 같습니다 #218

Open
gina0605 opened this issue Sep 29, 2020 · 8 comments
Labels
backend HW2 question Further information is requested specification issues related with specification of assignments

Comments

@gina0605
Copy link
Member

문제 상황

POST /api/v1/user/participant/ 를 구현하는 과정에서 발견한 현상입니다. accepted의 값을 넘겨주지 않았음에도 is_valid() 결과가 True였습니다. 이 현상을 테스트하기 위해 새로운 model과 serializer를 만들어보았습니다.

테스트

새로운 app booleanfield를 만들어 테스트했습니다.

booleanfield/models.py

from django.db import models

class TestModel(models.Model):
    bf = models.BooleanField()

booleanfield/serializers.py

from rest_framework import serializers
from booleanfield.models import TestModel

class TestSerializer(serializers.ModelSerializer):
    bf = serializers.BooleanField(required=True)
    class Meta:
        model = TestModel
        fields = ('bf',)

python manage.py shell

>>> from booleanfield.serializers import TestSerializer
>>> from django.http import QueryDict
>>> 
>>> d = {}
>>> TestSerializer(data=d).is_valid()
False
>>> qd = QueryDict()
>>> print(qd)
<QueryDict: {}>
>>> TestSerializer(data=qd).is_valid()
True

컴퓨터 환경

settings.py는 제공받은 waffle_backend의 settings.py에서 INSTALLED_APPSTIME_ZONE만 수정했습니다.
아래는 제 파이썬 환경입니다.

Package              Version
-------------------- -------
asgiref              3.2.10
Django               3.1
django-debug-toolbar 2.2
djangorestframework  3.11.1
mysqlclient          2.0.1
pip                  20.2.2
Pygments             2.7.1
pytz                 2020.1
setuptools           50.0.0
sqlparse             0.3.1
wheel                0.35.1

질문

  • 왜 QueryDict를 넘겨줬을 때 bf 값이 없는데도 is_valid()가 True일까요?
  • 왜 python dictionary를 넘겨줬을 때는 결과가 다를까요?
  • POST /api/v1/user/participant/ request를 할 때, Body에 데이터를 넘겨주면 ViewSet에서 request.data가 QueryDict였고, 데이터를 아무것도 넘겨주지 않으면 request.data가 빈 dictionary였습니다. 왜 request.data의 type이 달라지나요?
@gina0605 gina0605 added backend HW2 question Further information is requested labels Sep 29, 2020
@gina0605
Copy link
Member Author

참고로 BooleanField 대신 NullBooleanField를 이용하자 is_valid() 값은 항상 False로, 제가 생각하기에 합당한 결과가 나왔습니다.

@YeonghyeonKO
Copy link
Member

혹시 request에 필요한 값을 날리시기 전에 정의한 serializer나 model에서 field가 required=False 또는 blank=True 설정은 없는 상황인가요?

@gina0605
Copy link
Member Author

@YeonghyeonKO 네, serializer에서는 required=True만 설정해두었고(사실 설정하지 않아도 default가 required=True인 것 같긴 합니다) model에서는 딱히 설정한 것이 없습니다.

@gina0605
Copy link
Member Author

자답합니다.

우선, http request를 보낼 때 body에 보내는 내용은 DRF의 Parser에서 처리하게 됩니다. 이를 통해서 request.dataQueryDict object가 들어있게 되는 것입니다. 다만, http request의 body가 없을 경우 QueryDict가 아니라 Python dictionary가 되는 것 같습니다.

BooleanField의 값이 없어도 is_valid를 통과하는 이유는 DRF BooleanField document를 보면 알 수 있습니다. HTML incode input의 경우 BooleanField의 값이 주어지지 않아도 False로 처리된다고 적혀있습니다.

When using HTML encoded form input be aware that omitting a value will always be treated as setting a field to False, even if it has a default=True option specified. This is because HTML checkbox inputs represent the unchecked state by omitting the value, so REST framework treats omission as if it is an empty checkbox input.

코드를 살펴보면, serializer의 각 field 값을 주어진 data에서 읽는 메소드인 get_value()가 있습니다.
BooleanField의 경우 default_empty_htmlFalse로 설정되어 있습니다. 그렇기 때문에 값을 입력하지 않아도 저절로 False로 처리되는 것입니다. NullBooleanField의 경우에는 default_empty_htmlempty로 설정되어 있습니다. empty는 DRF에서 정의한 class로, 입력된 값이 없는 경우를 의미합니다. (None 자체가 유의미한 값일 수 있기 때문에, 이와 구분하기 위해 존재합니다.) required=True이고 get_value()에서 값이 empty일 경우 is_valid() 값이 False가 됩니다.

TLDR

  • QueryDict는 html_input이고, BooleanField는 html_input에 대해 값이 없을 경우 False로 설정됩니다.
  • Python dictionary는 html_input이 아니가 때문에 False로 설정되지 않고, is_valid() 값이 False입니다.
  • request.data의 Type이 QueryDictdictionary 두 가지가 나오는 이유는 DRF Parser와 관련된 내용입니다.

@gina0605
Copy link
Member Author

도큐멘트에 적혀있는 내용인데 한참 헤맸네요ㅜㅜ 과제는 NullBooleanField를 이용해 해결했습니다.

@YeonghyeonKO
Copy link
Member

아 sql에서 db 보셨으면 모든 accepted가 false로 되는 이유를 설명해주신거 같습니다. 저도 비슷한 상황이 왔었는데 처리만 했었지 이유까지는 알아보지 않았는데 감사합니다!

@davin111
Copy link
Member

davin111 commented Sep 30, 2020

과제 2 당시 이 부분의 명세가 불명료한 지점이 있었던 것 같은데, 일단 의도는 POST /api/v1/user/POST /api/v1/user/participant/ request의 body에서 accepted는 optional하고, 없는 경우 True라는 것이었습니다. accepted는 optional하고 default로 한 쪽의 값을 갖는 것이 일반적인 서비스의 동작을 고려했을 때 좀 더 자연스러운 구현이라고 생각되고, 반대로 accepted가 없을 때 400이어야 한다는 명세도 없었긴 했습니다.

어쨌든 과제 2의 이 부분에 대해서는 어느 쪽으로 구현하셨어도 자연스러운 것으로 받아들이도록 하겠습니다. 다만 과제 3을 진행할 때는, #221 과 관련해 optional한 것으로 취급하고, default 값을 True인 것으로 수정해주세요.

@davin111 davin111 reopened this Sep 30, 2020
@davin111 davin111 added the specification issues related with specification of assignments label Sep 30, 2020
@davin111
Copy link
Member

@gina0605 @YeonghyeonKO 관련 내용 과제 2, 과제 3에도 추가했습니다! 감사합니다. f2d6639

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend HW2 question Further information is requested specification issues related with specification of assignments
Projects
None yet
Development

No branches or pull requests

3 participants