Django

[DRF] Validation-유효성 검증

Y0un9Ki 2024. 3. 24. 02:57

어제 건의사항 페이지를 만들기 위해서 api를 짯다. 몇일에 걸쳐 잠을 줄인 결과 api짜는 것이 이해가 되고 조금 수월해 졌다.
너무 뿌듯하다!!!!!

그렇기에 django drf에서 건의사항(Post라고 app을 만들었다.)api를 만들면서 필자가 작성했던 validataion에 대해서 설명을 해놔야겠다.

 

Validation란 데이터 베이스에 저장하는 과정, Serializer에서 들어온 데이터가 유효한지 우리가 만들어놓은 필드에 잘 정의가 되어 있는 데이터가 들어오는지에 대해서 검증을 하는 것이다.

 

django에서는 model에서 유효성을 검증하는 방법과 Serializer에서 유효성을 검증하는 과정을 넣게 된다.

 

필자가 만들어 놓은 Post api를 기반으로 정리를 해봐야 겠다. 필자가 만든 api에 대해서도 블로그에 작성을 할 예정이다!!!

 

python manage.py startapp post

 

post라는 app을 생성했다. 이 다음 우리가 시작한 앱에 대해서 setting에 추가를 해줘야 하는데 이는 다음에 상세하게 적어 놓도록 하겠다.

 

# post/models.py

from django.db import models

# Create your models here.
class Post(models.Model):
    username = models.CharField(max_length=10)
    title = models.CharField(max_length=100, blank=False)
    content = models.TextField(blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['username']

 

post라는 앱 밑에 models.py에 Post에 들어올 모델을 정의를 해줬다. 이때 models라는 모듈의 Model을 이용해서 커스텀 필드를 생성해 줬다. @@

 

models.py에서의 유효성 검증

 

model에서 유효성을 검증하는 방법이 존재한다. 가장 간단한 방법은 모델을 정의할 때 모델에 필드 타입에 대해 정의해주는 것이다.

 

Model의 Field Type에 대한 설명이다.

CharField 제한된 문자열 필드 타입. 최대 길이를 max_length 옵션에 지정해야 한다.
문자열의 특별한 용도에 따라 CharField의 파생클래스로서,
이메일 주소를 체크를 하는 EmailField,
IP 주소를 체크를 하는 GenericIPAddressField,
콤마로 정수를 분리한 CommaSeparatedIntegerField,
특정 폴더의 파일 패스를 표현하는 FilePathField, 
URL을 표현하는 URLField 등이 있다.
TextField 대용량 문자열을 갖는 필드
IntegerField 32 비트 정수형 필드.
정수 사이즈에 따라 BigIntegerField, SmallIntegerField 을 사용할 수도 있다.
BooleanField true/false 필드.
Null 을 허용하기 위해서는 NullBooleanField를 사용한다.
DateTimeField 날짜와 시간을 갖는 필드.
날짜만 가질 경우는 DateField, 시간만 가질 경우는 TimeField를 사용한다.
DecimalField 소숫점을 갖는 decimal 필드
BinaryField 바이너리 데이타를 저장하는 필드
FileField 파일 업로드 필드
ImageField FileField의 파생클래스로서 이미지 파일인지 체크한다.
UUIDField GUID (UUID)를 저장하는 필드

 

Model의 Field Option 설명이다.

null (Field.null) null=True 이면, Empty 값을 DB에 NULL로 저장한다. DB에서 Null이 허용된다.
예: models.IntegerField(null=True)
blank (Field.blank) blank=False 이면, 필드가 Required 필드이다.
blank=True 이면, Optional 필드이다.
예: models.DateTimeField(blank=True)
primary_key (Field.primary_key) 해당 필드가 Primary Key임을 표시한다.
예: models.CharField(max_length=10, primary_key=True)
unique (Field.unique) 해당 필드가 테이블에서 Unique함을 표시한다.
해당 컬럼에 대해 Unique Index를 생성한다.
예: models.IntegerField(unique=True)
default (Field.default) 필드의 디폴트값을 지정한다.
예: models.CharField(max_length=2, default="WA")
db_column (Field.db_column) 컬럼명은 디폴트로 필드명을 사용하는데, 만약 다르게 쓸 경우 지정한다.

 

이걸로 model의 기본으로 정의를 해서 이에 따른 형태로 받을 수 있게 된다.

 

django의 DRF에서는 Validators를 지원하는데 DFR에서 유효성을 체크하는데 도움을 준다.

간단히 Validators를 사용하는 코드를 만들어보자.

 

from django.db import models
from django.core.validators import MinValueValidator

# Create your models here.
class Post(models.Model):
    username = models.CharField(max_length=10, validators=[MinValueValidator(2)])
    title = models.CharField(max_length=100, blank=False)
    content = models.TextField(blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['username']

 

django.core.validators에서부터 MinValueValidator를 import를 한다면 username의 최솟값의 대한 유효성을 검증할 수 있게 된다.

밑에는 django.core.validators로부터 어떤 것을 import할 수 있는지에 대한 옵션이다.

  • MinValueValidator와 MaxValueValidator: 숫자 필드의 최소값과 최대값을 검증합니다.
  • MinLengthValidator와 MaxLengthValidator: 문자열 필드의 최소 길이와 최대 길이를 검증합니다.
  • EmailValidator: 이메일 형식의 유효성을 검증합니다.
  • URLValidator: URL 형식의 유효성을 검증합니다.
  • RegexValidator: 정규 표현식을 사용하여 사용자 정의 유효성을 검증합니다.
  • FileExtensionValidator: 파일 확장자를 검증합니다.
  • BaseValidator: 사용자 정의 validator를 만들기 위한 기본 클래스입니다.

이것을 사용하면 우리가 데이터가 들어올 때 이것에 맞추어서 들어오는지에 대해 유효성을 검증 할 수 있다. 하지만 이렇게 한다면 에러메세지를 지정할 수 없고(프론트엔드랑 협업을 할 때 에러메세지에 대한 키값과 value값을 맞추는 것 또한 굉장히 중요하다!!!) 사실 지향되는 방법이라고는 할 수 없다. 그렇기에 이제 Serializer에서 유효성을 검증하는 방법을 알고자 한다.

 

Serializer.py에서 유효성 검증

Serializer에서는 계속 해서 강조하지만 model(DB)에 데이터를 처리하고 변환하며 저장, 수정하는 작업을 진행하게 된다.

그렇기에 Serializer는 데이터를 model(DB)에 저장을 하거나 수정을 하기에 만약 유효성 검증을 한다면 Serializer에서 해주는 것이 지향되는 방향이다.

가장 많이 쓰이는 방법은 함수로 정의해서 유효성 검증을 해주는 방법이다.

 

내가 짠 코드를 예시로 보여주겠다.

 

validate VS validated_<필드명>

# post/serializer.py

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'
        # fields = ['username', 'title', 'content'] 이렇게 fields를 선택해서 가져오기도 가능
        
    def validate(self, attrs):
        if len(attrs.get('username', '')) <2:
            raise serializers.ValidationError({'message' : '이름을 2글자 이상으로 해주세요'})
        if len(attrs.get('title', ''))>100:
            raise serializers.ValidationError({'message': 'title이 100글자를 넘지 않게 해주세요'})
        if len(attrs.get('content', ''))>500:
            raise serializers.ValidationError({'message' : '내용이 너무 길어요. 500글자 미만으로 작성해주세요'})
        return attrs
    
    def create(self, validated_data):
        return Post.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        instance.username = validated_data.get('username', instance.username)
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        
        instance.save()
        return instance

 

위와 같이 필자는 validate라는 함수를 만들어서 view에서 is_valid()라는 함수가 호출이 되면 유효성을 검증하는 함수(validate or validated_<필드명>)가 작동하도록 코드를 만들었다.

 

여기서 attrs랑 validated_data라는 매개변수가 무엇인지 궁금할 수도 있기에 설명을 적어 놓겠다.

그 전에 attrs.get('username', '')에서 두번째 인자가 ''인게 궁금할 수 있기에 이것또한 추가 설명을 해놨다.

 

attrs.get('username', '')에서 ''는 username 필드가 값이 없을 때를 대비하여 기본값으로 사용되는 것이다. attrs.get('username', '')는 username 필드에 대한 값을 가져오는데, 만약 username 필드가 존재하지 않는다면(값이 없다면) 빈 문자열('')을 반환하게 된다.  이를 통해 username 필드가 값이 없을 때에도 안전하게 처리할 수 있다.

 

  • attrs : attrs 매개변수는 전체 필드의 데이터를 포함하는 데이터로 모든 model fields의 키값에 맞게 할당되어서 들어온 데이터이다. 예시를 들어보자면 username에는 'ray'가 들어오고 title에는 '제목', content에는 '내용'이라는 값이 프론트에서 백으로 넘어온 데이터라고 했을 때 attrs=['username' : "ray", "title" : "제목", "content" : "내용"] 이런식으로 데이터가 들어오게 된다.
  • validated_data : validated_data는 유효성 검증을 성공적으로 맞힌 데이터이다. 예시를 들어보자면 위에서 attrs할 때 봤던 ['username' : "ray", "title" : "제목", "content" : "내용"]은 모두 유효성을 통과할 수 있는 데이터 이기에 validated_data = ['username' : "ray", "title" : "제목", "content" : "내용"]이 되는 것이다.
  • value : validate_<필드명> 메서드를 사용할 때는 해당 필드의 값을 나타내는 value라는 이름의 매개변수를 전달받는다. validate_<필드명> 메서드는 해당 필드명의 값을 자동으로 전달받는다. 예시를 들면 validated_username(self, value)로 만들었을 때 여기서 value값은 username의 value값인 'ray'가 자동으로 들어오게 된다. 

 

여기서 알아야 할 점이 있는데 유효성을 검증하는 함수를 만드는 방식은 위와 같이 validate라는 클래스내 메서드로 만들거나 아니면 validate_<필드명>으로 클래스 내에 메서드를 만들 수 있는데 이것의 차이점validate라는 함수는 model fields를 모두 가지고 와서 유효성 검증을 하는 방식이고, validate_<필드명>은 model의 필드들 중에 하나씩 가지고 와서 유효성 검증을 하는 방식이 된다. 이것은 django에서 form을 가지고 할 때, clean_<필드명>으로 메서드를 만들어주는 것과 유사하다.

 

두개의 예시 코드를 모두 보여줘야겠다. 왜냐하면 이걸 찾아보면서 필자는 매우 헷갈렸었다. 

# validate 방식
def validate(self, attrs):
        if len(attrs.get('username', '')) <2:
            raise serializers.ValidationError({'message' : '이름을 2글자 이상으로 해주세요'})
        if len(attrs.get('title', ''))>100:
            raise serializers.ValidationError({'message': 'title이 100글자를 넘지 않게 해주세요'})
        if len(attrs.get('content', ''))>500:
            raise serializers.ValidationError({'message' : '내용이 너무 길어요. 500글자 미만으로 작성해주세요'})
        return attrs
        
# validate_<필드명> 방식
def validate_username(self, value):
    if len(value) < 2:
        raise serializers.ValidationError({'message': '이름을 2글자 이상으로 해주세요'})
    return value

def validate_title(self, value):
    if len(value) > 100:
        raise serializers.ValidationError({'message': 'title이 100글자를 넘지 않게 해주세요'})
    return value

def validate_content(self, value):
    if len(value) > 500:
        raise serializers.ValidationError({'message': '내용이 너무 길어요. 500글자 미만으로 작성해주세요'})
    return value

 

여기서의 validate_<필드명>의 value값은 위에 리스트로 설명을 해놨다.

서로 같은 방식이며 쓰고 싶은것을 사용하면 된다.

 

DRF의 공식문서에서 유효성 검증을 Serializer를 정의할 때부터 해주는 validators가 존재하는 데 이것은 내가 잘 사용을 하지 않아서 공식문서를 링크로 띄워놓겠다.

https://www.django-rest-framework.org/api-guide/validators/

 

Validators - Django REST framework

 

www.django-rest-framework.org

이것을 참조하면 된다.

 

오늘은 필자가 만들어 놓은 Post api에서 유효성 검증을 하는 방법에 대해서 블로그를 올렸다. 원래는 DRF의 view에 대한 글을 써야 하는데 순서상 유효성검증이 먼저여서 먼저 작성을 하게 되었다. 다음에는 DRF의 view에 대해 필자가 만든 코드와 설명에 대해서 블로그 포스팅을 해야겠다.

@+@+@+@+

'Django' 카테고리의 다른 글

[DRF] VIEW - Mixins  (4) 2024.03.26
[DRF] VIEW - APIVIEW  (0) 2024.03.25
[DRF] serializer와 view의 역할  (0) 2024.03.22
[DRF] Serializer CRUD - DRF TUTORIALS  (0) 2024.03.22
(Django) 1.Serializer(직렬변환)  (0) 2024.03.19