original source : http://www.deekras.com/django-validations-during-form-processing.html

form validation에 대한 documentation이 좀 이해하기 힘든데 이 블로그는 간단명료학 잘 정리했다.

This post is mostly based on the Django Docs on Form and Field Validation. I reformatted the information in a way that feels easier to use.

There are 3 types of cleaning methods that are run during form processing. These are normally executed when you call the is_valid() method on a form.  (is_valid() runs validation routines for all fields on the form. When this method is called, if all fields contain valid data, it will:

  • return True
  • place the form’s data in its cleaned_data attribute.)

In general, any cleaning method can raise a ValidationError if there is a problem with the data it is processing; it passes the relevant information to the ValidationError constructor.

Steps of validation

The methods below are run in the order given, one field at a time. That is, for each field in the form (in the order they are declared in the form definition). Then the form.clean(), or its override, is executed regardless if the previous methods have raised errors. If the Field.clean() method raises a ValidationError, its field-specific cleaning methods are not called. However, the cleaning methods for all remaining fields are still executed.

Normally, the clean() method will be run and it will take care of the first three validations (to_python(), validate(), run_validators()). But you can customize any of them, and when the clean() method is executed, it will run the customized method.

1. to_python() method on a Field

  • WHAT IT DOES: It coerces the value to correct datatype and raises ValidationError if that is not possible. This method accepts the raw value from the widget and returns the converted value.
  • EXAMPLE: a FloatField will turn the data into a Python float or raise a ValidationError.
  • HANDLES ERRORS: raises ValidationError on any error
  • RETURNS: returns the converted value.

2. validate() method on a Field

  • WHAT IT DOES: handles field-specific validation that is not suitable for a validator. It takes a value that has been coerced to correct datatype and raises ValidationError on any error.
  • HANDLES ERRORS: raises ValidationError on any error
  • RETURNS: This method does not return anything and shouldn’t alter the value.
  • NOTES: You should override it to handle validation logic that you can’t or don’t want to put in a validator.

3. run_validators() method on a Field

  • WHAT IT DOES: runs all of the field’s validators
  • HANDLES ERRORS: aggregates all the errors into a single ValidationError.
  • RETURNS:
  • NOTES: You shouldn’t need to override this method.

4. The clean() method on a Field subclass.

  • WHAT IT DOES: This is responsible for running to_python, validate and run_validators in the correct order and propagating their errors.
  • HANDLES ERRORS: If, at any time, any of the methods raise ValidationError, the validation stops and that error is raised.
  • RETURNS: This method returns the clean data, which is then inserted into the cleaned_data dictionary of the form.

5. The clean_<fieldname>() method in a form subclass – where <fieldname> is replaced with the name of the form field attribute.

  • WHAT IT DOES: This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is.
  • HOW TO USE: This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).
  • HANDLES ERRORS:
  • RETURNS: the cleaned value obtained from cleaned_data – regardless of whether it changed anything or not.

6. The Form subclass’s clean() method.

NOTES: Also note that there are special considerations when overriding the clean() method of a ModelForm subclass. (see the ModelForm documentation for more information)

  • WHAT IT DOES: This method can perform any validation that requires access to multiple fields from the form at once.
  • EXAMPLE: Checks that if field A is supplied, field B must contain a valid email address and the like.
  • HOW TO USE: Since the field validation methods have been run by the time clean() is called, you also have access to the form’s errors attribute which contains all the errors raised by cleaning of individual fields.
  • HANDLES ERRORS: Note that any errors raised by your Form.clean() override will not be associated with any field in particular. They go into a special “field” (called __all__), which you can access via the non_field_errors() method if you need to. If you want to attach errors to a specific field in the form, you need to call add_error().
  • RETURNS: This method can return a completely different dictionary if it wishes, which will be used as the cleaned_data.

Raising ValidationError examples:

if not flag:
    raise ValidationError('Please submit flag') –  a simple example
    raise ValidationError(_('text: %(flag)s'), 
                            code='no flag', 
                            params={'flag': '42'},)

multiple errors can be created as a list

    raise ValidationError([
        ValidationError(_('Error 1'), code='error1'),
        ValidationError(_('Error 2'), code='error2'),
    ])

Writing Validators

There are many builtin validators that match the field type (ex: EmailValidator for EmailField). Those validators can be customized too. (ex: class EmailValidator([message=None, code=None, whitelist=None])

Here’s a sample custom validator:

from django.core.exceptions import ValidationError

def validate_even(value):
   if value % 2 != 0:
      raise ValidationError('%s is not an even number' % value)

Then, this validator can be used for any fields when setting up the models:

class MyModel(models.Model):
   even_field = models.IntegerField(validators=[validate_even])

It can also be used for forms:

class MyForm(forms.Form):
   even_field = forms.IntegerField(validators=[validate_even])

Validators will not be run automatically when you save a model, but if you are using a ModelForm, it will run your validators on any fields that are included in your form.

form validation doc https://docs.djangoproject.com/en/3.0/ref/forms/validation/

참고 블로그) 내용이 official doc보다 간결하다. http://www.deekras.com/django-validations-during-form-processing.html

These are normally executed when you call the is_valid() method on a form. There are other things that can also trigger cleaning and validation (accessing the errors attribute or calling full_clean() directly), but normally they won’t be needed. 

(form validation은 form의 is_valid()를 통해 수행된다. form에 있는 errors에 접근하거나 full_clean()을 통해 수행되기도 한다. full_clean()는 model validation에서 사용되는 method이름과 같다.)

  •  to_python() 스트링을 python data type으로 전환한다.
  • validate() method on a Field handles field-specific validation that is not suitable for a validator. 
  • run_validators() method on a Field runs all of the field’s validators and aggregates all the errors into a single ValidationError. You shouldn’t need to override this method.
  • clean() method on a Field subclass is responsible for running to_python(), validate(), and run_validators() in the correct order and propagating their errors. This method returns the clean data, which is then inserted into the cleaned_data dictionary of the form.
  • clean_<fieldname>() method is called on a form subclass. This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is. This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).

기본적으로 위와 같은 순서로 진행되며 각 단계에서 무제가 발생하면 ValidationError를 raise한다.

Raising ValidationError

# Good
ValidationError(_('Invalid value'), code='invalid')

# Bad
ValidationError(_('Invalid value'))

# Good
ValidationError(
    _('Invalid value: %(value)s'),
    params={'value': '42'},
)

# Bad
ValidationError(_('Invalid value: %s') % value)

# Good
ValidationError(
    _('Invalid value: %(value)s'),
    params={'value': '42'},
)

# Bad
ValidationError(
    _('Invalid value: %s'),
    params=('42',),
)

Wrap the message with gettext to enable translation: _() 를 말한다.

# Good
ValidationError(_('Invalid value'))

# Bad
ValidationError('Invalid value')

Putting it all together:

raise ValidationError(
    _('Invalid value: %(value)s'),
    code='invalid',
    params={'value': '42'},)

Raising multiple errors

# Good
raise ValidationError([
    ValidationError(_('Error 1'), code='error1'),
    ValidationError(_('Error 2'), code='error2'),
])

# Bad
raise ValidationError([
    _('Error 1'),
    _('Error 2'),
])

기존 field를 extends해서 새로운 field를 만들고 새로운 validation rule을 지정하는 방법

1방법)

from django.core import validators
from django.forms import CharField

class SlugField(CharField):
    default_validators = [validators.validate_slug]
slug = forms.SlugField()

2방법)

slug = forms.CharField(validators=[validators.validate_slug])

validator만드는 방법 https://docs.djangoproject.com/en/3.0/ref/validators/

form의 field에 validation을 customize하는 방법 (to_python , validate)

from django import forms
from django.core.validators import validate_email

class MultiEmailField(forms.Field):
    def to_python(self, value):
        """Normalize data to a list of strings."""
        # Return an empty list if no input was given.
        if not value:
            return []
        return value.split(',')

    def validate(self, value):
        """Check if value consists only of valid emails."""
        # Use the parent's handling of required fields, etc.
        super().validate(value)
        for email in value:
            validate_email(email)
class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

Use MultiEmailField like any other form field. When the is_valid() method is called on the form, the MultiEmailField.clean() method will be run as part of the cleaning process and it will, in turn, call the custom to_python() and validate() methods.

form 단위에서 field validation 을 customize 하는 경우

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean_recipients(self):
        data = self.cleaned_data['recipients']
        if "fred@example.com" not in data:
            raise forms.ValidationError("You have forgotten about Fred!")

        # Always return a value to use as the new cleaned data, even if
        # this method didn't change it.
        return data

이미 fileld단위의 validation이 끝났기 때문에 cleaned_data에 접근 가능하다.

Cleaning and validating fields that depend on each other (form 단위에서 여러개의 fields을 이용해서 validation하는 경우)

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject:
            # Only do something if both fields are valid so far.
            if "help" not in subject:
                raise forms.ValidationError(
                    "Did not send for 'help' in the subject despite "
                    "CC'ing yourself."
                )

In this code, if the validation error is raised, the form will display an error message at the top of the form (normally) describing the problem.

The call to super().clean() in the example code ensures that any validation logic in parent classes is maintained. If your form inherits another that doesn’t return a cleaned_data dictionary in its clean() method (doing so is optional), then don’t assign cleaned_data to the result of the super() call and use self.cleaned_data instead:

def clean(self):
    super().clean()
    cc_myself = self.cleaned_data.get("cc_myself")
    ...
from django import forms
class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject and "help" not in subject:
            msg = "Must put 'help' in subject when cc'ing yourself."
            self.add_error('cc_myself', msg)
            self.add_error('subject', msg)

original source : https://youtu.be/nMP6fDNtbfY

image

Drawable obj는 내부에 getIntrinsicHeight(), getIntrinsicWidth() 함수들을 구현해서 외부에서 이 함수를 호출했을때 Drawable이 실제 그려지는 부분의 크기를 되돌린다.

Drawable obj는 내부에 setLevel(), setState() 함수들을 구현해서 각 level, state에 맞게 drawable obj가 행동하도록 만든다.

Drawable.Callback interface는 invalidateDrawable(),scheduleDrawable(),unscheduleDrawable()들을 구현해야 한다. 참조) https://developer.android.com/reference/android/graphics/drawable/Drawable.Callback

image
image

=========================================================

.

.

아래는 custom view내부에서 drawable을 사용하는 경우를 보여주고 있다

image

=========================================================

.

.

Drawable 자체를 만드는 과정

image
image
image

=========================================================

.

.

image

개발자가 새로운 Layout을 만들고 그 안에서 새로누 LayoutParams를 만들어 사용하는 경우 Layout class 안에 generatedLayouParams(), generateDefaultLayoutParams(), checkLayoutParams()를 구현해 주어야 한다.

image

orginal source : https://programmer.help/blogs/android-custom-view-foundation-onmeasure-onlayout.html

Android Custom View Foundation onMeasure & onLayout

Official Guidelines: https://developer.android.com/guide/topics/ui/custom-components.html

Method introduction

  • onMeasure()   Used to measure the width and height of view
  • onLayout()   Used to control the position of subviews

onMeasure

In this method, the width of the view is measured and then the height of the view is determined. It should be noted here that the width of the sub-view is measured, not its own.

The complete method is like this.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

Two parameters are passed in, which are the measurements of the size of the current View by the parent layout. It contains wide and high values and measurement modes, which can be obtained using the View.MeasureSpec class.

int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);

It goes without saying that size is px. There are three measurement modes, as shown in the table below.

Measurement mode

  • MeasureSpec.EXACTLY Precise size, parent layout has given a clear size, such as match_parent or 50dp, all of which are precise.
  • MeasureSpec.AT_MOST Adaptive, the parent layout gives a maximum, and the child view adapts itself to the size, but cannot exceed the maximum, such as setting the width to wrap_content
  • MeasureSpec.UNSPECIFIED There is no clear size, the parent layout does not give any value, and the child view is free to play! uuuuuuuuuuu It should be used in a layout like a list adapter

Well, knowing the meaning of this parameter, we can then measure the width of the self-view. In general, if you need to measure subviews, you must inherit ViewGroup or its subclasses.

Several methods are provided in ViewGroup to facilitate simple measurement operations:

  • measureChild()
    Measure the self-view according to the width and height measurements given by the parent layout
  • measureChildWithMargins()
    Measure the self-view according to the given parent layout width and height measurement data and the occupied width and height values, and consider the outer margin of the sub-view. (Margin Layout Params also needs to be implemented if this measurement method is used)

Or you can set the measurements using the measure method of the subview.

Here’s a simple example, inheriting ViewGroup

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    for (int i = 0, count = getChildCount(); i < count; i++){
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE){
            //Measurement Subview
            measureChild(child, widthMeasureSpec,heightMeasureSpec);
        }
    }
    //In onMeasure, this method must be called to set the final measurement width and height.
    setMeasuredDimension(
                MeasureSpec.getSize(widthMeasureSpec),
                MeasureSpec.getSize(heightMeasureSpec));
}

onLayout

This method is called after onMeasure, where the position of the child view is set. The layout() method is called once for all the subviews, and the task is completed.

The simplest example is to stack the layout from the top corner without considering any other factors.

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 0, count = getChildCount(); i < count; i++){
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE){
            child.layout(0,0,child.getMeasuredWidth(),child.getMeasuredHeight());
        }
    }
}

Example

Combining the above two methods gives rise to a view that looks like FrameLayout (there’s still a big gap in detail! )

public class CustomFrameLayout extends ViewGroup {

    public CustomFrameLayout(Context context) {
        super(context);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        for (int i = 0, count = getChildCount(); i < count; i++){
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE){
                measureChild(child, widthMeasureSpec,heightMeasureSpec);
            }
        }

        setMeasuredDimension(
                MeasureSpec.getSize(widthMeasureSpec),
                MeasureSpec.getSize(heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0, count = getChildCount(); i < count; i++){
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE){
                child.layout(0,0, child.getMeasuredWidth(),child.getMeasuredHeight());
            }
        }
    }
}

Of course, the official FrameLayout is far more complicated than the examples I wrote, because there are many other things to consider, such as the margin problem, the size of the outlook, gravity of sub-views, left-right swaps to consider when internationalizing, and so on. So when you can’t fully grasp the customized view, don’t easily inherit ViewGroup directly. You can find a suitable subclass to inherit and make a little modification. It will be much easier!

android animation의 종류로는 기본적으로 3가지가 있다.

1. frame animation

2. tween animation (view animation)

3. property animation

.

.

.

.

.

.

.

.

frame animation

frame animation은 xml화일에 <animation-list> 와 <item>을 이용하여 만든다. 이를 ImageView에 src에 연결한다. 코드상에서는 ImageView를 통해서 AnimationDrawable obj형태로 받아오게된다.

res/drawable에 xml 형태로 저장된다.

참조) android docs AnimationDrawable https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable

예시코드)

<!-- Animation frames are wheel0.png through wheel5.png
     files inside the res/drawable/ folder -->
 <animation-list android:id="@+id/selected" android:oneshot="false">
    <item android:drawable="@drawable/wheel0" android:duration="50" />
    <item android:drawable="@drawable/wheel1" android:duration="50" />
    <item android:drawable="@drawable/wheel2" android:duration="50" />
    <item android:drawable="@drawable/wheel3" android:duration="50" />
    <item android:drawable="@drawable/wheel4" android:duration="50" />
    <item android:drawable="@drawable/wheel5" android:duration="50" />
 </animation-list>
// Load the ImageView that will host the animation and
 // set its background to our AnimationDrawable XML resource.
 ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
 img.setBackgroundResource(R.drawable.spin_animation);

 // Get the background, which has been compiled to an AnimationDrawable object.
 AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();

 // Start the animation (looped playback by default).
 frameAnimation.start();

.

.

.

.

.

.

.

.

tween animation (view animation)

  • view의 display형태만 바꾸는 방법이며 실제로 view의 위치는 바뀌지 않는다. 사용자가 이동된 위치에서 터치를 해도 반응하지 않는다. 
  • animation은 translate, scale, rotate, alpha 4가지가 있다.
  • xml 형태로 만든 animation은 res/anim 폴더안에 넣는다.
  • tween animation은 view, drawable (ShapeDrawable, BitmapDrawable), layout에 적용가능하다.
  • tween animation은 내부적으로 Matrix, Transformation class를 사용한다. 
  • AnimationUtils의 loadAnimation()를 이용해서 xml형태의 Animation을 가져온다.

tween animation에서 만들때 사용가능한 animation tag들은 아래와 같다.

image
image

.

.

image
image
image
image

.

.

<set>을 이용하여 여러가지 animation을 동시에 구현할수 있다.

image
image
image
image

.

.

일반적으로 사용되는 위의 4가지 animation이 아닌 다른 움직임을 구현하기 위해서는 custom animation을 이용한다. 이는 Animation을 extends하고 필요한 method를 override해서 구현한다.

image

.

.

xml형태로 만들어진 tween animation은 layoutAnimation에 적용가능하다. layoutAnimation을 이용하면 layout내의 view들이 처음 등장할때 animation대로 등장하게된다.

https://youtu.be/qMQar9UNqjU?t=2311

image

.

.

activity간 전환에 적용되는 animation도 tween animation (view animation) 이다.

app 내에서 모든 window의 생성, 사라짐에 사용되는 animation를 지정할때는 theme에서 사용될 view animation을 지정해 주면된다.

image
image

단하나 주의점은 animation의 작동시간을 지정해도 0.25초안에 마무리 되게 된다. 

image
image

.

.

fragment 전환간 animation에도 tween animation (view animation)이 사용된다.

image
image

.

.

추가적으로 ViewSwitcher (TextSwitcher, ImageSwitcher), ViewFlipper는 tween animation (view animation)을 이용한다. 맨 아래부분 참조

.

.

.

.

.

.

.

.

property animation

ValueAnimator, ObjectAnimator를 이용하는 방법이 있다. 기본적으로 ObjectAnimator는 ValueAnimator를 기반으로 extends 해서 만들어졌다.

ValueAnimator는 특정범위에안에서 변화된 값을 만들어낸다. 이때 addUpdateListener를 통해 연결된 listener에서 onAnimationUpdate() 안에서 변화되는 값에 접근할수 있다.

Animator, ObjectAnimator를 xml로 만드는 경우에는 res/animator에 만든다.

아래는 code에서 ValueAnimator를 이용한 경우

image

.

아래는 xml를 이용한 ValueAnimator 경우

image
image

.

ObjectAnimator를 code에서 이용하는 법과 xml을 통해 이용하는 방법을 보여주고 있다.

image
image

.

.

animator 를 이용한 animation을 AnimatorSet으로 묶어서 순서를 가지고 수행하거나 동시에 수행하는 방법을 설명하고 있다.

image
image

.

.

Animator를 이용해서 layout내의 view가 등장하거나 사라지거나 다른 view가 등장 사라짐에따라 위치가 변경되는 경우에 사용되는 animation을 만들수 있으며 이를 LayoutTransition이라고 한다.

image

.

.

Animator를 이용한 animation인데 기존의 경우 ValueAnimator가 특정범위에안에서 변화된 값을 만들어내고 addUpdateListener를 통해 연결된 listener에서 onAnimationUpdate() 안에서 변화되는 값을 통해 view의 속성값을 변화시켰다. 아래의 경우도 같은 효과를 가진다. 다만 ViewPropertyAnimator (support library를 사용한겨우 ViewPropertyAnimatorCompat)를 생성하기위해 View.animate (또는 support 라이브러리의 경우 ViewCompat.animate) 에 속성값을 animate할 view를 arg로 받게 된다.

image
image

.

.

RecyclerView내의 item이 생성되거나 사라질때 애니메이션을 이용할수 있는데 이때에도 Animator를 기반으로한 property animation을 사용한다. 

참조 영상 ) RecyclerView 내의 item 생성소멸시 animation 사용예 https://youtu.be/8sQmuafiaAQ?t=220  

image
image

.

.

AdapterViewAnimator, AdapterViewFlipper, StackView는 property animator를 기반으로 하고 있다.

.

.

.

.

.

이름이 ViewAnimator이지만 기본적으로 ViewSwitcher (TextSwitcher, ImageSwitcher), ViewFlipper는 tween animation (view animation)을 이용한다.

AdapterViewAnimator, AdapterViewFlipper, StackView는 property animator를 기반으로 하고 있다.

실제 사용 예시)

구글 앱엔진에서 사용자 정의(custom) 도메인을 설정하는 과정

3번째 이미지에 나와있는 내용(DNS정보)을 웹서버에 추가 해주여야 한다.