안녕하세요 장고 is_valid의 동작방식이 궁금합니다.
조회수 2960회
if self.is_valid():
return redircet('/')
위와 같은 코드에서 is_valid()가 내부적으로 해당 폼의 clean()을 호출해서 유효성 검사를 하는거라고 알고있는데 clean() 같은경우는 return 값이 cleaned_date 라는 dict자료 던데요,
is_valid는 어떻게 True False 를 반환할수 있는거죠? clean() 안에서 add.error 라던지 errors 의 dict 값에 요소가 추가되면 False를 반환하도록 되어있는건가요?
아시는분 답변 부탁드립니다. 이해 안가고 너무 궁금하네요 정중히 부탁드리겠습니다 . 읽어주셔서 감사합니다. 남은시간도 좋은하루 보내시길 바라겠습니다.
-
(•́ ✖ •̀)
알 수 없는 사용자
1 답변
-
머.. 너무 당황치 마시고 소스를 차근차근 보면서 같이 알아보시죠. 일단
is_valid()
입니다.def is_valid(self): return self.is_bound and not self.errors
사실 이 구문 자체는 무조건
bool
이 돌아옵니다. 왜냐하면and
연산자가 쓰이고 있기 때문이지요. 뭔진 몰라도self.is_bound
는True
여야 하고self.errors
는 Falsy해야 유효한 폼이 되는 거네요.
그럼
self.errors
가 뭐길래?@property def errors(self): if self._errors is None: self.full_clean() return self._errors
self.errors
는 기본적으로self._errors
를 반환하는 프로퍼티군요. (self._errors
가 하필None
일 때는self.full_clean()
을 한번 돌리긴 하지만, 그 결과를 반환하는 건 아니고요.)이쯤에서 일단 질문자님의 의문 ― "
self.clean()
은dict
를 반환하는데 어떻게is_valid()
는bool
을 반환하지?" ― 은 어느 정도 해결이 되었으리라고 생각됩니다.is_valid()
는 그 안에서 무슨 난리굿판이 벌어지든 결국에는True
아니면False
가 나오는 메소드입니다.
그러면 이제 추가 의혹 ― "
clean()
안에서add.error
라던지errors
의dict
값에 요소가 추가되면 False를 반환하는 건가? 뭐지?" ― 을 확인해 보도록 하지요. 지금까지 본것에 따르면is_valid()
는 결국self._errors
가 뭔가 값이 없지 않을 때False
가 되는 건데요.그러면 이제 질문은
self._errors
가 뭐냐 하는 문제가 됩니다.def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None): # 중략 self._errors = None # clean() 이 호출된 뒤에 에러들을 여기 저장한다.
영어 주석을 하나 번역했습니다.
self._errors
에 에러들을 저장한다는군요. 어떻게? 검색을 해보죠.def add_error(self, field, error): # 중략 for field, error_list in error.items(): if field not in self.errors: # 중략 if field == NON_FIELD_ERRORS: self._errors[field] = self.error_class(error_class='nonfield') # <-- 이런거 else: self._errors[field] = self.error_class() # <-- 이런거 self._errors[field].extend(error_list) if field in self.cleaned_data: del self.cleaned_data[field] # <-- 이런거
알고 계신 대로
self._errors
는 오류들의 리스트입니다.self._errors[field]
구문을 보면 명백하죠.그러면
self.add_error()
는 언제 호출될까요?def _clean_fields(self): for name, field in self.fields.items(): # 중략 try: # 중략 except ValidationError as e: self.add_error(name, e) # <-- 여기
그리고
self._clean_fields()
는 언제 호출되는가 하면...def full_clean(self): # --> 음? 이거 어디서 본 적 있는 메소드 같은데? # 중략 if self.empty_permitted and not self.has_changed(): return self._clean_fields() # <-- 여기 self._clean_form() self._post_clean()
아! 이게 이렇게 도는 것이군요. 여기서부터는 django form의 validation 처리 순서도를 그리실 수 있을 것이라 생각됩니다.
요컨대 django forms의 유효성 검사도 대다수 validators들의 방법을 따르는 것 같습니다. 오류의 목록을 저장/업데이트하는 변수/속성이 있고, 그 목록이 비어 있는지에 따라 유효/무효를 판별해 주는 변수/속성이 또 따로 있는 것이죠.
왜 그렇게 해야 하느냐? 그냥 아래 코드처럼 심플하게 하면 되지 않느냐? 하신다면...
if (무효조건1) { // 얼핏 보면 아무 문제 없는 구조이지만 실제로는 문제가 있다. alert(경고1); return false; } if (무효조건2) { // ex) 무효조건2 구문이 그 자체로 오류가 있거나 무조건 참이 나오거나 코드 인젝션 가능한 버그 코드라면 이 구간은 그야말로 대책 없다. alert(경고2); return false; } if (무효조건3) { // ex) 무효조건1~3을 모두 위반한 사용자는, 받아야 할 모든 경고를 받기 위해, 최소 3번의 재시도를 해야 한다. 사용자는 3배 이상 화딱지가 나게 된다. alert(경고3); return false; } return true; // 그래서 이 방식으로 유효성 판별하는 코드들은 사실 재설계해야 한다.
답변이 도움이 되었을지 모르겠네요.
댓글 입력