Django Web Framework/Django Form for Web

post로 받은 데이터를 form에 넣으면 어떻게 처리가 될까?

bluebamus 2021. 7. 2.

기본적으로 함수형 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/

 

[Form]의식의 흐름대로 정리하는 장고 Form

장고의 정말 큰 장점 중 하나는 Form이라고 생각한다. 물론 여러 좋은 점들이 있지만 장고 폼을 통해서 값을 변경 / 검사를 하고 심지어 HTML 코드로도 랜더링 할 수 있기 때문인데, 장고에서 자주

jinmay.github.io

 

is_valid 에러 관련 :

https://hashcode.co.kr/questions/8710/%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94-%EC%9E%A5%EA%B3%A0-is_valid%EC%9D%98-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D%EC%9D%B4-%EA%B6%81%EA%B8%88%ED%95%A9%EB%8B%88%EB%8B%A4

 

안녕하세요 장고 is_valid의 동작방식이 궁금합니다.

if self.is_valid(): return redircet('/') 위와 같은 코드에서 is_valid()가 내부적으로 해당 폼의 clean()을 호출해서 유효성 검사를 하는거라고 알고있는데 clean() 같은경우는 return 값이 cleaned_date 라는 dict자료

hashcode.co.kr

파일 의존성 관련 :

https://blog.hannal.com/page10/

 

Kay on the rails ·

25 Dec 2020 탈진한 상반기 상반기는 탈진하도록 몸과 마음을 썼다. 2분기에 들어서는 시점에 탈진 징조를 느꼈고, 6월 중순부터 회복과 충전하는 시간을 가졌다. 내게 익숙하고 편한 몸무게로 감량

blog.hannal.com

 

'Django Web Framework > Django Form for Web' 카테고리의 다른 글

ModelForm 기본형  (0) 2021.07.02

댓글