본문 바로가기
WebFramework/Python Django

[Python Django] 14. Django Custom User Form (1) - AbstractUser

by Rosmary 2024. 4. 23.
728x90
반응형

 

 

 

 

 

 

이번 포스팅에서는 일반적인 ModelForm이 아닌, Django의 계정과 관련된 ModelForm에 대해 다루어보려한다.  Django에서는 Django 내부에서 사용할 사용자 정보의 관리와 인증을 django 내부에 built-in된 별도의 모듈을 사용하여 관리한다.

 

 

 

django 프로젝트 폴더 내 INSTALLED_APPS에 기본으로 지정된 모듈 중 admin가 django 사용자와 관련되 모듈이며, 이 모듈로 인해 Django에서 별도로 사용자와 관련된 DB Model을 생성하지 않고도 바로 사용자와 관련된 어플리케이션을 제작하는 것이 가능하다.

 

코드 내에 save()나 create_user()와 관련된 부분은 DB 관련된 주제를 다룰 때 다시 언급할 예정이다.

 

 

Django 공식 문서를 보면, 단순히 django.contrib.auth.models에서 제공하는 User Model을 사용하여 손쉽게 사용자를 만들 수 있게 되어 있는데, 이 Model은 ID에 해당하는 Username과, 비밀번호, 이름으로 구성된 단순한 모델이다. django의 manage.py에서 제공하는 createsuperuser 명령어 역시, 이 모델을 따르기 때문에 superuser 계정을 생성할 때, username과 비밀번호 외에도 이메일을 입력하라는 문구가 나타나는 것이다.

 

 

 

그런데, 모든 웹 페이지의 사용자들이 django의 기본 모델을 따르지는 않기 때문에 django의 기본 사용자 모듈을 사용하게 되면 문제가 발생할 수 밖에 없다. 예를 들면, 택배 회사의 웹 같은 경우 사용자가 택배를 받기 위한 주소 정보나 연락처 정보가 반드시 계정 정보에 포함되어야 할 것이고, SNS 같은 경우 프로필에 올릴 이미지 파일도 포함되어야 할 것이니 말이다. 따라서 Django는, 필요한 경우 기본으로 제공하는 Model을 상속받아 새 Model로 커스터마이징하여 사용하는 것을 권고하고 있다.

 

Django에서는 Custom User Model 사용을 위해, 아래의 방법을 사용할 것을 권고한다.

 

- django.contrib.auth.models.User (Model의 1대 1 맵핑을 통한 새 커스텀 모델 생성)

- django.contrib.auth.models.AbstractUser (Custom 모델에 상속받아 사용)

- django.contrib.auth.models.AbstractBaseUser (Custom 모델에 상속받아 사용)

 

 

이번 포스팅에서는 Django User Model 의 커스터마이징 방법 중, AbstractUser를 상속받아 사용하는 방법에 대해서만 알아보려한다. 

 

 

 

 

1. 프로젝트 구성

 

프로젝트 구성은 매우 간단(?)하다. 사용자 계정을 만들고, 조회하고, 수정하고, 삭제하는 기능과(CRUD) 로그인 로그아웃 기능을 가지는 웹 페이지를 만들려고 한다. Django 프로젝트 생성 후, accounts라는 이름의 앱을 생성하고, settings.py와 urls.py의 기본 구성을 미리 진행한다. 필자의 경우 URL 을 아래와 같이 구성했다.

 

 

* 계정 정보 조회 페이지: localhost:8000/ (로그인, 회원가입(계정 비로그인 시) 및 로그아웃(계정 로그인 시) 버튼 추가)

* 계정 로그인 ; localhost:8000/sigin

* 계정 로그아웃: localhost:8000/signout

* 계정 정보 추가: localhost:8000/signup

* 계정 정보 수정 페이지: localhost:8000/modify

* 계정 정보 삭제: localhost:8000/delete

 

 

대략적인 계획에 따라 accounts 앱의 urls.py는 다음과 같이 작성했고,

 

 

 

views.py는 아래와 같이 작성했다.

 

 

 

여기까지 완료되었다면, 각 페이지에 필요한 templates 파일들은 별도로 폴더를 만들어 보관하고, 이 폴더의 경로를 settings.py의 TEMPLATES_DIRS에 등록해주면 오늘 포스팅을 위한 프로젝트 생성은 완료된다.

 

 

Django에서 기본으로 User Model을 사용한다면 극단적으로 views.py 파일만 약간 수정하더라도 계정과 관련된 폼은 쉽게 구성이 가능하다. 우선 manage.py로 기본 User Model에 대해 DB migration을 진행하고 회원 가입 페이지만 살짝 만들어보자.

 

python manage.py makemigration
python manage.py migrate

 

 

명령어 수행 결과는 아래와 같이 나타나며

 

 

 

명령어 결과로 프로젝트 폴더 내에 생성된 sqlite DB 파일을 sqlite3로 열어보면 아래와 같이 계정 기본 모델 테이블인 auth_user가 공식 문서에서 보았던 것과 동일한 형태의 테이블을 가지고 있음을 확인할 수 있다.

 

https://docs.djangoproject.com/en/4.2/ref/contrib/auth/ 페이지를 참고하자

 

 

따라서 forms.py 파일에서 ModelForm을 생성하면서 모델을 Django의 기본 모델을 바라보게 만든다면

 

좌측부터 forms.py, views.py, signup.html 파일이다.

 

 

Django의 기본 User Model에 걸맞는 폼이 웹 브라우저에 나타나도록 만들 수 있다.

가입하기 참 싫어지는 폼이다.

 

 

나타난 모양을 보면 알겠지만, 딱 봐도 Django에서 제공하는 회원가입 폼에는 불필요한 내용도 많고, 추가해야 할 내용도 많다. 즉, 이 폼을 바로 적용해서 계정 관련 페이지를 만드는 것은 영 무리라는 것이다. 

 

 

 

 

2. AbstractUser 클래스 상속으로 Custom Model 만들기

 

 

(1) 개요

 

폼이 멋이 없어서 그런건지는 모르겠지만, Django의 공식 문서는, 아무리 기본 Model Form이 사용에 적합하다고 하더라도, Custom Model을 생성하여 사용할 것을, 그리고 Custom Model과 관련된 세팅은 프로젝트의 초반에 진행할 것을 권고하고 있다.

 

 

 

세팅할 내용은 크게 세 가지인데, 첫 번째는 Custom Model을 AbstractUser 클래스를 상속받아 models.py에 생성하는 것이고,

 

 

 

두 번 째는 Django의 settings.py에 AUTH_USER_MODEL 값을 추가해 주는 것이다. 추가하는 값은 문자열이며 "[APP_NAME].[MODELNAME]" 형태로 작성한다. 

 

 

 

이 두 과정은 Django의 DB Migration을 진행하기 전에 실행해야 하며, 만약 이미 Migration을 진행한 상태라면 DB 파일과 Migration 부산물인 python 파일을 싹 지운 상태에서 진행해주어야 한다.

 

 

 

마지막은 - 사실 필자 생각에 크게 중요하지는 않지만 - admin 사이트에 새로 생성한 User Model이 보이도록 admins.py에 등록을 진행해주는 것이다.

 

 

 

세 가지 세팅 중 첫 번째와 세 번째는 그나마 쉽게 이해가 가능하지만, 두 번 째는 처음 접하면 이해가 어렵다. 사실 Django는 AUTH_USER_MODEL이라는 값이 기본 model인 django.contrib.auth.model.User를 바라보도록 미리 지정되어 있다. 따라서 Custom으로 인해 Model이 변경되는 경우, Django에게 "원래 참조하던 기본 모델 말고 내가 만든 모델 참조해!" 라고 알려주는 용도라 보면 된다. 

 

즉, AbstractUser 클래스를 사용한 커스터마이징을 위해서는 Django의 기본 UserModel이 아닌 커스터마이징 모델을 생성해야하며, 생성한 모델을 Django가 바라볼 수 있도록 만들어주어야 한다. 

 

 

 

 

공식 문서에서 진행하라는대로 수행한 뒤, 화면에 나타나는 값을 확인해보자. 우선은 별다른 필드의 추가나 변경은 없이 그대로 진행하려 한다.

 

좌측: setting.py   /   우측:  models.py

 

 

 

Migration까지 완료가 되면, DB 파일 내부의 테이블에는 기존에 User Model을 담당하던 테이블인 auth_user가 사라지고, accounts_accounts 라는 새 모델이 생성된 것이 확인된다.

 

 

 

그런데... 테이블 스키마를 보니, 기존에 사용한 Django의 기본 User와 동일한 필드를 가지고 있는 것처럼 보인다. 언뜻 보면 뭔가 잘못하고 있는 것 같다는 생각이 들기 쉬운데, 소스 파일을 열어보면 납득이 된다. 

 

 

 

Django의 기본 User Model인 django.contrib.auth.model.User 역시, AbstractUser 클래스를 상속받는다. 그리고 이 AbstractUser 클래스는 지금까지 Django의 계정 관련 폼에서 보았던 모든 필드 정보가 저장되어 있다. 단, User 클래스와 지금 필자가 생성하려는 Custom Model 사이의 차이점이라면, User 클래스는 어떠한 필드도 정의되지 않아 AbstractUser의 필드를 고스란히 사용해야하는 반면에, 커스터마이징 Model에서는 AbstractUser 클래스에서 제공하는 필드 외에 필요한 필드를 추가할 수 있다는 점이다.

 

현재는 Custom Model에 pass 만 작성되어 있기 때문에, forms.py에 정의한 Form Class의 model을 Accounts를 바라보도록 변경한다고 하더라도 화면에 출력되는 모양은 그대로 유지된다.

 

 

 

 

 

(2) Custom Model 필드 수정

 

이제 Models 필드를 수정해보자. 필자는 username, password, email 그리고 휴대폰 연락처 정도만 Field로 설정하여 간단하게 모델을 만들어보려한다. 

 

우선 username과 password, email 필드는 상속받은 AbstractUser 클래스에도 존재하는 내용이라 굳이 모델을 정의하지 않아도 되지만, 필자는 AbstractUser의 username이 최대 길이를 150자까지 허용받는다는 것에 조금 불만이 있기 때문에 username 필드는 새로 정의해주었다. 또한 Email 필드는 AbstractUser에서 blank를 허용하지만, 필자는 허용할 생각이 없기 때문에 이 필드 역시 Custom 모델에서 새로 정의했다.

 

AbstractUser에 없는 휴대폰 연락처는 AbstractUser에서 정의되지 않은 내용이라, 반드시 custom Model에 정의해주어야 한다.

 

여기에, 회원 가입일시와 수정일시 표시를 위해 create_dt, update_dt라는 필드도 DateTimeField로 추가했다. 결과는 아래와 같이 나타난다.

 

 

 

Custom User Model의 작성은 이게 끝이다(응?).

 

 

현재 상태에서 서버를 구동하고 회원 가입 페이지로 이동하면, Custom Model에 정의된 필드 값 외에 AbstractUser에 정의된 일부 필드값도 나타난다.

 

 

 

이 문제는 ModelForm의 Meta.fields 값이 __all__로 설정되어 있어 나타나는 현상이기 때문에, 필요한 필드만 추출하여 fields에 지정해주면 해결된다. 

 

help_text와 error_message는 예전 포스팅에서 설명한 내용이라 넘어간다.

 

 

이제 회원 가입 Page의 form이 입력되면, views.py 파일에서 해당 내역이 콘솔에 출력되도록 해보려 한다.

 

비밀번호 Field에 widget 추가하는 것 깜빡했다...

 

 

입력 폼에 별도의 에러없이 Custom Model에 값이 잘 전달됨을 확인할 수 있다.

 

 


 

 

계정과 관련된 부분은 보통 회원 가입, 회원 로그인, 수정과 같은 기능이 반드시 따라오는데, 이들 역시 사용자로부터 입력값을 받아야 하기 때문에 Form을 사용할 수 밖에 없다. 그런데, 단순히 ModelForm을 상속받아 이들 기능을 구현하는 것은 문제가 있다.

 

일단, 회원 로그인 기능부터 한 번 살펴보자. 회원 로그인은 사용자로부터 username과 비밀번호를 전달받는 것 까지는 Form으로도 충분히 가능하다. 별도로 Form을 만들고 forms.ModelForm을 상속받아 처리하면 될 것이기 때문에. 하지만, 들어온 계정에 대한 인증 처리는 매우, 매우 복잡해지는데, 아래의 결과를 보자

 

비밀번호 관련 Field는 Password로 입력받도록 Form에 widget을 추가했다.

 

 

조금 전에 작성한 Custom Model의 경우, 필자가 Unique 속성을 True로 지정(안하면 AbstractUser 때문에 어차피 에러는 난다...)했는데, 회원 가입을 하는 것도 아닌 로그인에서 Unique 속성에 걸린다는 것은 뭔가 잘못되어도 한참 잘못된 것이다. 그럼, AbstractModel로 실컷 Custom Model은 만들어놓고도 사용하지 못하는게 아닌가라는 의문이 들 수도 있지만...다행히 Django는 이러한 문제를 해결할 수 있는 계정 연관 Form들을 제공한다. 

 

 

-  회원 로그인 관련    : django.contrib.auth.forms.AuthenticationForm

-  회원 가입 관련        : django.contrib.auth.forms.UserCreateForm

-  회원 정보 수정 관련: django.contrib.auth.forms.UserChangeForm 

 

 

생각보다 지면이 길어져서.. 이들 Form의 사용 방법은 다음 포스팅에서 작성하려 한다.

 

 

 

End.

 

 

 

반응형

댓글