Django Form 소스 분석의 BaseForm 검증 논리

18153 단어 원본 코드django
인용문
Django에서 Form의 주요 기능은 입력 검증과 템플릿에서의 전시로 나뉜다.먼저 Form의 소스 정의를 살펴보겠습니다.
class Form(six.with_metaclass(DeclarativeFieldsMetaclass, BaseForm)):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.

BaseForm의 검증 논리 분석과 관련된 것이기 때문에 저는 본 블로그에서fields에 있는 데이터를 요청에서 어떻게 얻는지 관심이 없습니다. 저는 데이터가 처리를 거쳐fields에 전달되었다고 가정합니다.(템플릿의 출력 형식 규범도 많이 언급하지 않음) 그리고 저는 인용문에서 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):
        self.is_bound = data is not None or files is not None
        self.data = data or {}
        self.files = files or {}
        self.auto_id = auto_id
        if prefix is not None:
            self.prefix = prefix
        self.initial = initial or {}
        self.error_class = error_class
        # Translators: This is the default suffix added to form field labels
        self.label_suffix = label_suffix if label_suffix is not None else _(':')
        self.empty_permitted = empty_permitted
        self._errors = None  # Stores the errors after clean() has been called.

        # The base_fields class attribute is the *class-wide* definition of
        # fields. Because a particular *instance* of the class might want to
        # alter self.fields, we create self.fields here by copying base_fields.
        # Instances should always modify self.fields; they should not modify
        # self.base_fields.
        self.fields = copy.deepcopy(self.base_fields)
        self._bound_fields_cache = {}
        self.order_fields(self.field_order if field_order is None else field_order)

양식 데이터 바인딩:Form.is_bound
바인딩 논리 판정은 초기화에서 다음과 같습니다.
 self.is_bound = data is not None or files is not None

이것은 다음과 같은 상황을 잘 설명한다.
>>> not_bound = exampleForm()
>>> not_bound.is_bound
False
>>> is_bound = exampleForm({})
>>> is_bound.is_bound
True

일반적으로 데이터 매개 변수는 Request입니다.POST or request.GET, files 매개변수는 request.FILES, 주의해야 할 것은 파일을 업로드할 때 Request를 요구합니다.method는 POST이고 enctype='multipart/form-data'여야 합니다.
양식의 유효성 검사 방법: Form.is_valid()
def is_valid(self):
        """ Returns True if the form has no errors. Otherwise, False. If errors are being ignored, returns False. """
        return self.is_bound and not self.errors

먼저 양식 데이터가 바인딩된 후에야 유효성을 검사할 수 있으며 양식이 유효성 검사 과정에서 raise Validation Error가 없을 때 True로 돌아갑니다.is_bound 속성은 내가 방금 이미 분석했기 때문에self를 중점적으로 본다.errors 속성입니다.
Form.errors
초기화에서 우리는 전혀 self가 없다는 것을 볼 수 있다.errors 이 속성은self.errors라는 개인 속성입니다.python에서 @property 사용법을 사용했기 때문입니다.
@property
    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

우리가 처음으로 form을 호출할 때is_valid () 또는 form에 액세스합니다.errors 속성이 있을 때self를 호출합니다.full_clean () 함수, 이외에 사유의self.errors. 기본적으로 form.errors는 필드를 키로 하고value는Validation Error 메시지를 포함하는 Error List의 사전입니다. 물론 우리가 필요로 하는 것은 메시지가 아니라 진정한Validation Error 실례일 수도 있습니다. 이때form을 호출할 수 있습니다.errors.as_데이터 (), 이외에form도 제공합니다.errors.as_json().(이 두 가지 방법은 다음 문장에서 출처를 언급할 것이다)
Form.full_clean()
fullclean ()에서 주로self. 호출됨clean_fields(),self._clean_form(),self._post_clean()은 폼에 대한 검증을 완성했다. 그 중에서 우리는 구체적인 필드에 대한 분석을 하지 않고 폼 전체의 검증을 중점적으로 분석한다.(즉, self. clean form()
 def full_clean(self):
        """ Cleans all of self.data and populates self._errors and self.cleaned_data. """
        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()

Form에서.full_clean () 방법에서 우리는 errors 속성이 실제로는 ErrorDict라는 것을 볼 수 있다. 우리는 위에서 언급한form을 간단하게 분석한다.errors.as_data().
Form.errors.as_data()
# ErrorDict.as_data()

def as_data(self):
        return {f: e.as_data() for f, e in self.items()}

위에서 언급한value는 ErrorList입니다.
# ErrorList.as_data()

def as_data(self):
        return ValidationError(self.data).error_list

여기서 ValidationError.error_list는 ValidationError 인스턴스가 포함된 list입니다.
Form._clean_form()
 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

그 중에서 clean 함수는form에서 제공하는 개발자가 스스로 정의할 수 있는 함수로 원시적인 정의에서 간단히self를 되돌려준다.cleaned_data.우리는 clean 함수를 덮어쓰고 전체 폼 차원의 검증 논리 사용자 정의를 제공할 수 있습니다. 이때self.cleaned_데이터는field의 필드 검증을 거쳤습니다.
    def clean(self):
        """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """
        return self.cleaned_data

그래서 form에서.clean ()에서 발생하는 모든 이상은self에 저장됩니다.errors에서 키는 입니다.all__의value에서 주의해야 할 것은 우리가Form을 직접 통과할 수 있다는 것이다.non_field_errors() 반환all__의 ErrorList를 참조하십시오. 이유all__자, 우리 다음으로self를 분석합시다.add_error 함수.
Form.add_error(field, error)
그 중에서 error는 문자열일 수도 있고,ValidationError의 실례일 수도 있습니다.
    def add_error(self, field, error):
        """
        Update the content of `self._errors`. The `field` argument is the name of the field to which the errors should be added. If its value is None the errors will be treated as NON_FIELD_ERRORS. The `error` argument can be a single error, a list of errors, or a dictionary that maps field names to lists of errors. What we define as an "error" can be either a simple string or an instance of ValidationError with its message attribute set and what we define as list or dictionary can be an actual `list` or `dict` or an instance of ValidationError with its `error_list` or `error_dict` attribute set. If `error` is a dictionary, the `field` argument *must* be None and errors will be added to the fields that correspond to the keys of the dictionary. """ if not isinstance(error, ValidationError): # Normalize to ValidationError and let its constructor # do the hard work of making sense of the input. error = ValidationError(error) if hasattr(error, 'error_dict'): if field is not None: raise TypeError( "The argument `field` must be `None` when the `error` " "argument contains errors for multiple fields." ) else: error = error.error_dict else: error = {field or NON_FIELD_ERRORS: error.error_list} for field, error_list in error.items(): if field not in self.errors: if field != NON_FIELD_ERRORS and field not in self.fields: raise ValueError( "'%s' has no field named '%s'." % (self.__class__.__name__, field)) 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]
  • 먼저 error가ValidationError의 실례인지 확인하고 그렇지 않으면 실례화한다.
  • 실례라면 이 실례가 error 인지 확인합니다dict, 그렇지 않으면 error를 하나의 키로field,value는errorlist의 사전입니다.필드가 None이면 키는 NON 입니다.FIELD_ERRORS, 기본값all__.
  • field 매개 변수가self에 없으면errors에서field가self에 존재하는지 판단합니다.필드가 존재하면 errors에 키를 필드로 만들고 ErrorList가 이 필드의value에 값을 부여하는 것을 실례화합니다.
  • field 매개 변수는self에 존재합니다.errors 시, 직접 extend 새로운 errorlist.
  • 마지막으로self.cleaned_데이터에서 이 필드를 삭제합니다.폼의 전시 초기화:Form.initial 폼에서 초기화된 매개 변수인 initial의 우선순위는 필드의 initial보다 높습니다. 아래의 원본 코드 분석을 통해 알 수 있듯이
    def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.initial.get(name, field.initial)
    처음에 나는Form이라고 생각했습니다.initial 이 속성은 데이터로 초기화된 매개 변수이지만, 나중에 생각해 보니 그렇지 않다. 단지 Field일 뿐이다.disabled를 사용할 때form은 initial에서 값을 가져옵니다. 이 인자는 전시용으로 사용되며 데이터의 기본값이 아닙니다.주의, 여기Form.initial은ModelForm과 구분하여django-forms-default-values-for-bound-forms를 참고해야 합니다.또한 Django 공식 문서에는 다음이 설명되어 있습니다.
    These values are only displayed for unbound forms, and they’re not used as fallback values if a particular value isn’t provided.
    마지막으로 나는 다시 한 가지 예를 제시한다.
    class QueryForm(forms.Form): limit = forms.IntegerField(required=False) offset = forms.IntegerField(required=False)
    >>> f = QueryForm(data={}, initial={'limit': 20, 'offset': 0})
    >>> f.is_bound
    True
    >>> f.is_valid()
    True
    >>> f.cleaned_data
    {'limit': None, 'offset': None}
    다음에 나는 다른 블로그에서 Field에 대한 원본 분석을 전개할 것이다.부족한 점이 있으면 삼가 가르침을 바랍니다.
  • 좋은 웹페이지 즐겨찾기