오히려 좋아..

상황이 나쁘게만 흘러가는 것 같을 때 외쳐보자.. .

궁금한 마음으로 포트폴리오 보기

Web Programming/Django

[장고백서] 필드에서 작성한 validator가 작동을 안할 때

junha6316 2021. 4. 9. 15:04

필드에 validators를 선언해  해당 필드 값에 제한을 둘 때가 있다.

가령 이름에 공백을 포함하고 싶지 않다면 해당 기능을 아래와 같이 구현할 수 있다.

#persons/models.py

from django.db import models
from .validators import space_validator

class Person(models.Model):
    
    name = models.CharField("이름", max_length=100, validators=[space_validator,])
#persons/validators.py

from django.core.exceptions import ValidationError


def space_validate(value):
    if len(value.split()) >=2:
        raise ValidationError(
        	"이름에 공백이 있습니다. 공백 제거 후 다시 저장해주세요"
        )

이런식으로 사용하면 django admin에서 Person 오브젝트를 저장하려고 이름에 공백이 있다면 경고와 함께 저장이 멈추게 된다.

django admin 오류 창

 

하지만 model에 선언했다고 해서 이 validator가 데이터 베이스 수준에서 이 값의 유효성을 검증하는 것은 아니다. 

그 단적인 예로 python manage.py shell 명령어로 shell을 켜 아래와 같이 한다면 오류없이 작동한다.


from persons.models import Person


Person.objects.create(name="박 준 하 다 이")

 

그러니 validator가 특별히 문제가 있는게 아니라 원래 장고가 그런 것이다.

그럼 이 validator는 django admin에서만 작동하는것인가? 그런건 아니다.

ModelForm을 이용한 구현에서는 추가한 validator를 이용해 값을 검증한다.

 

하지만 진짜 데이터베이스 레벨에서 이러한 값의 유효성을 제한하고 싶을 떄는 방법이 없을까?

장고에서는 이러한 기능을 위해 Constraint라는 기능을 제공한다. Constraint는 meta 클래스에서 사용되며 아래와 같이 사용할 수 있다.

class 모델(models.Model):
	
    
    class Meta:
        constraints =[
        	CheckOutConstraint(
                check = "검증 로직을 작성",
                name = "해당 유효성 검사의 이름" 
            )
        ]

다양한 Constraint 클래스가 존재하지만 여기서는 CheckoutConstraint를 다뤄 보도록 하겠다.

다른 Constraint는 장고 공식문서에서 확인 하도록 하자.

docs.djangoproject.com/en/3.2/ref/models/constraints/

 

Constraints reference | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

CheckOutConstraint는 값이 검증 로직에 맞는지 체크하는 Constraint로 check 부분에 Q 이용해 검증 로직을 작성한다.

만약 해당 규칙을 어겼을 경우 IntegrityError를 발생시킨다.

 

아래의 예는 startDate(시작 날짜)가 endDate(끝나는 날짜)보다 앞서도록 값의 제약을 둔 예시이다.

class Event(core_model.TimeStampedModel):

    ''' Event Model Definition '''

    name = models.CharField(max_length=150, unique=True, verbose_name="행사명")
    manager = models.ForeignKey("users.User", related_name='Event', null=True, verbose_name=("담당자"), on_delete=models.CASCADE,help_text="담당자를 배정해주세요 담당자가 없다면 호스트 탭으로 가서 담당자를 만들어주세요")
    startDate = models.DateTimeField("행사 시작일", help_text="행사 등록 시작일으로 설정해주세요. 메일 발송전에는 열려 있어야 합니다.")
    endDate = models.DateTimeField("행사 종료일", help_text="행사가 완전히 종료되는 날로 설정해주세요.")
   
    class Meta:
        verbose_name_plural = "이벤트"
        constraints =[ 
            CheckConstraint(
                check=Q(endDate__gt=F('startDate')),
                name="StartDate_Earlier_Than_EndDate"
            )
        ]