[Django] unique=True 조건과 blank=True 조건을 동시에 허용하는 모델 필드 만들기

gramm·2021년 4월 24일
2

django

목록 보기
2/5
post-thumbnail

참고 자료

https://docs.djangoproject.com/en/3.2/ref/models/fields/

https://stackoverflow.com/questions/17257031/django-unique-null-and-blank-charfield-giving-already-exists-error-on-admin-p

https://stackoverflow.com/questions/15422606/django-model-email-field-unique-if-not-null-blank/15422763




장고에서 모델을 설계할 때,

i) 유일성 조건을 만족하며 (값이 겹치지 않으며)
ii) 폼에서 값을 입력하지 않아도 되는

필드를 만들고 싶은 경우가 있다. 이때는 해당 필드에 unique=True 조건과 blank=True 조건을 같이 주면 된다.


  • unique=True 조건은 해당 필드의 값이 겹치지 않게 하는 조건이다.

  • blank=True 조건은 해당 필드가 폼에서 입력될 때 빈 상태로 저장되는 것을 허용하는 조건이다.


# models.py

class User(AbstractUser):
    email = models.EmailField(max_length=200, unique=True, blank=True)  

그런데 unique=Trueblank=True 조건을 동시에 쓸 때 한 가지 문제가 있다.


유저 모델의 이메일 필드에 unique=True 조건과 blank=True 조건이 있고, 회원 가입 시에 이 값을 입력받는 상황을 생각해 보자. A는 blank=True 조건이 있으므로, 이메일 필드값을 따로 입력하지 않고 회원 가입에 성공한다. 그런데 이때 이메일 필드에는 null값이 아니라 빈 문자열이 저장된다. 따라서 A가 회원 가입을 끝낸 후, B가 이메일 필드값을 입력하지 않고 회원 가입을 하려고 하면, unique=True 조건에 걸려 가입이 되지 않는다. 빈 문자열도 하나의 유일한 값으로 인식되어, 유일성 조건에 걸리기 때문이다.


그렇다면 빈 값을 입력받으면서도 unique=True 조건에 걸리지 않게 하려면 어떻게 해야 할까? null=True 조건을 주면 된다.


  • null=True 조건은 해당 필드의 값이 데이터베이스에 null값으로 저장되는 것을 허용하는 조건이다.

여러 필드가 빈 문자열을 값으로 가지면 유일성 조건에 걸린다. 반면 여러 필드가 null값을 가질 때는 유일성 조건에 걸리지 않는다. 따라서 특정 필드에 null=True 조건을 주면,

i) 유일성 조건을 만족하며 (값이 겹치지 않으며)
ii) 폼에서 값을 입력하지 않아도 되는

필드를 만들 수 있다.


# models.py

class User(AbstractUser):
    email = models.EmailField(max_length=200, 
                              unique=True, blank=True, null=True)

문자열 기반이 아닌 필드에서는 위와 같은 방식으로 문제가 해결된다. 하지만 CharField, TextField, EmailField와 같은 문자열 기반 필드에서는 여전히 문제가 해결되지 않는다. 문자열 기반 필드는 null=True 조건이 있더라도, 폼에서 빈 값을 입력받으면 DB에도 빈 문자열로 저장된다. 따라서 null=True 조건이 없을 때와 마찬가지로 유일성 조건에 걸리게 되기 때문이다.


해결 방법은 모델 클래스 내에서 빈 값을 입력받았을 때 null값으로 바꾸어주는 함수를 정의하는 것이다.


# models.py

class User(AbstractUser):
    email = models.EmailField(max_length=200, 
                              unique=True, blank=True, null=True)
    
    # unique=True 조건을 만족시키기 위해,
    # 이메일 필드의 값이 빈 문자열인 경우 null값으로 바꾼다.
    def clean(self):
        if self.email == "":
            self.email = None     

이제 문자열 기반 필드에서도 유일성 조건을 만족하는 동시에, 값을 입력하지 않아도 되도록 설정할 수 있다.

profile
I thought I introduced

0개의 댓글