post로 받은 데이터를 form에 넣으면 어떻게 처리가 될까?
기본적으로 함수형 view의 post는 다음과 같이 구현이 된다.
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save()
return redirect(post)
그렇다면 PostForm에 request.post를 넣어주면 어떤일이 발생될까?
먼저 상속관계를 확인하면 다음과 같다.
form : BaseForm -> Form
modelform : BaseForm -> BaseModelForm -> ModelForm
둘다 baseform으로 시작되는 만큼 대부분 코드는 baseform에 구현되어 있다.
BaseForm
class BaseForm:
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.is_bound = data is not None or files is not None
self.data = {} if data is None else data
self._errors = None
@property
def errors(self):
if self._errors is None:
self.full_clean()
return self._errors
request.POST는 init 함수 첫번째 인자로 들어가 self.data라는 인스턴스 변수 값이 된다.
그리고 is_valid 함수를 호출하여 유효성 검사를 수행한다.
이후 is_valid 함수를 통해 유효성 검사를 한다.
is_valid
# django/forms/forms.py
def is_valid(self):
return self.is_bound and not self.errors
is_bound 결과는 bool 값이된다. 에러가 있는지 판단하여 둘 중 하나라도 false가 되는 경우 유효성 검사는 중단된다.
만약 문제 없이 데이터가 있는 경우 is_bound는 true가 되고 self._error의 값은 초기값인 None이므로
다음 단계인 full_clean 함수로 넘어간다.
full_calen
def full_clean(self):
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()
cleaned_data를 빈 딕셔너리로 초기화한 후 _clean_fields, _clean_form, _post_clean 함수를 호출한다.
full_calen -> _clean_fields 함수
# django/forms/forms.py
def _clean_fields(self):
for name, field in self.fields.items():
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value) # 필드 객체의 clean 함수 호출
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)
# django/forms/fields.py
# 필드 객체가 가진 clean함수이다. form 객체의 clean 함수과 혼동하지말자.
def clean(self, value):
value = self.to_python(value)
self.validate(value)
self.run_validators(value)
return value
self.fields로부터 폼의 필드 이름과 객체를 반복 순회하면서 clean 함수를 호출한다.
clean 함수는 각 필드의 validator를 이요해 값을 검증하고 반환한다.
필드의 clean함수로부터 반환된 값은 필드이름을 key로 cleaned_data에 저장된다.
만약, 개발자가 'clean_%s' 형식의 함수를 만든게 있다면, 이 함수의 결과를 cleaned_data에 다시 저장한다.
예시 : clean_%s
class PostForm(forms.Form):
title = forms.CharField()
def clean_title(self):
title = self.cleaned_data.get('title')
# title 값 처리
# 길이, 대소문자, 특수문자 등의 처리포함
return title
full_calen -> _clean_fields 함수
# django/forms/forms.py
def _clean_form(self):
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
def clean(self):
return self.cleaned_data
개발자가 form에 clean을 오버라이딩 했다면, 해당 삼수를 호출하여 받아온 인스턴스 변수 cleaned_data를
_clean_fields 함수의 내부 인스턴스 변수인 cleaned_data에 다시 저장한다.
에러가 없다면, self.cleaned_data에 최종 clean()된 값이 들어간다.
예시 : clean in form
class PostForm(forms.Form):
title = forms.CharField()
def clean_title(self):
pass
def clean(self):
pass
실행순서 : title field의 clean() -> clean_title() -> 폼 객체의 clean()
full_calen -> _post_clean 함수
# django/forms/forms.py
def _post_clean(self):
"""
An internal hook for performing additional cleaning after form cleaning
is complete. Used for model validation in model forms.
"""
pass
# django/forms/models.py
def _post_clean(self):
opts = self._meta
exclude = self._get_validation_exclusions()
for name, field in self.fields.items():
if isinstance(field, InlineForeignKeyField):
exclude.append(name)
try:
self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
except ValidationError as e:
self._update_errors(e)
try:
self.instance.full_clean(exclude=exclude, validate_unique=False)
except ValidationError as e:
self._update_errors(e)
if self._validate_unique:
self.validate_unique()
일반 폼에서는 _post_clean 함수는 동작하지 않고 모델폼에서 모델의 유효성 검사를 위해 사용되는 함수다.
* 만약 form에 파일이 있는 경우, update를 구현할 때에는 다음과 같이 사용해야 한다.
form = BoardUpdateForm(request.POST or None, request.FILES or None, instance=board)
본문 참고 :
https://jinmay.github.io/2019/11/13/django/django-form-is-valid-mechanism-brief/
is_valid 에러 관련 :
파일 의존성 관련 :
https://blog.hannal.com/page10/
'Django Web Framework > Django Form for Web' 카테고리의 다른 글
ModelForm 기본형 (0) | 2021.07.02 |
---|
댓글