장고는 기본적으로 User Model 을 제공한다.
하지만 기본적으로 제공되는 필드들로만은 원하는 서비스를 만들지 못한다.
주소 , 전화번호 , 프로필 사진 등을 등록할려면 Custom User Model 을 사용해야 한다.
이번 포스트는 장고 커스텀 유저모델을 사용하는 방법에 대해서 알아보도록 한다.
django 공식 홈페이지
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#a-full-example
참고 및 출처 :
https://dev-yakuza.posstree.com/ko/django/custom-user-model/
프로젝트 생성
django-admin startprojet custom_user_model
account 앱 생성
python manage.py startapp accounts
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, name, email, address, phone_number, password=None):
if not name:
raise ValueError('must have user email')
if not email:
raise ValueError('must have user email')
user = self.model(
name=name,
email=self.normalize_email(email),
address=address,
phone_number=phone_number
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, name, email, address, phone_number, password):
user = self.create_user(
name=name,
email=self.normalize_email(email),
address=address,
phone_number=phone_number,
password=password
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=255,unique=True,)
address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = ['email','address','phone_number']
def __str__(self):
return self.name
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
@property
def is_staff(self):
return self.is_admin
커스텀 유저 모델 만들기 위해서는 (BaseUserManager, AbstractBaseUser) 두 클래스가 필요하다.
BaseUserManager 클래스는 유저를 생성할 때 사용하는 헬퍼(Helper) 클래스이다.
실제 모델(Model)은 AbstractBaseUser을 상속받아 생성하는 클래스이다.
헬퍼(Helper) 클래스인 class UserManeger(BaseUserManager)는 두가지 함수를 가지고 있다.
첫번째 파라미터가 username 파라미터이다.
나는 name을 username 으로 사용하기 때문에 username 이 아닌 name을 넣는다.
class User(AbstractBaseUser):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=255,unique=True,)
address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
실제 모델인 User Model 을 살펴보면,
name, email, address, phone_number, is_active, is_admin 필드를 가지고 있다.
원하는 필드를 더 추가 할 수도 있다.
또한 is_active, is_admin 필드는 장고(django)의 유저 모델(User Model)의 필수 필드입니다.
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
@property
def is_staff(self):
return self.is_admin
커스텀 유저 모델을 기본 유저 모델(Model)로 사용하기 위해서는 구현해야 하는 부분이다.
장고의 관리자 페이지 사용하는 Form을 수정하기 위해서는 자체적인 우리의 커스텀 유저 모델(Custom User Model)에 맞는 Form 을 생성한다.
accounts/form.py
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from .models import User
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password',widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation',widget=forms.PasswordInput)
class Meta:
model = User
fields = ('name','email','address','phone_number')
def clean_password2(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self,commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('name','email','address','phone_number')
def clean_password(self):
return self.initial["password"]
사용자 암호를 ReadOnlyPasswordHashField()으로 가져와서 화면에 표시해 줄 예정.(수정 못함)
accounts/admin.py
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .forms import UserCreationForm,UserChangeForm
from .models import User
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('name','email','address','phone_number','is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None,{'fields':('name','email','password')}),
("Personal info",{'fields':('address','phone_number')}),
("Permissions",{'fields':('is_admin',)})
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('name','email', 'address','phone_number', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
admin.site.unregister(Group)
좀 더 자세히 살펴 보면
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
우리가 만든 Form 을 admin에 적용한다.
list_display = ('name','email','address','phone_number','is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None,{'fields':('name','email','password')}),
("Personal info",{'fields':('address','phone_number')}),
("Permissions",{'fields':('is_admin',)})
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('name','email', 'address','phone_number', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
관리자 페이지에 어떻게 나타낼지 설정한다.
admin.site.register(User, UserAdmin)
admin.site.unregister(Group)
최종적으로 admin 에 등록하고, 기본적으로 제공하는 Group 은 사용하지 않도록 설정한다.
우리가 만든 커스텀 유저 모델을 인증 모델(Athentication User Model)로 등록한다.
장고에서 기본적으로 인증에 사용될 기본 모델로 설정된다.
custom_user_model/settings.py
...
AUTH_USER_MODEL = 'account.User'
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration account.0001_initial is applied before its dependency user.0001_initial on database 'default'.
django에서 기본으로 제공하는 user model을 쓰지 않고
커스텀 유저 모델을 설정하는 과정에서 생긴 문제였는데, 조금 헤맸다.
stackoverflow를 뒤져보니
settings.py의 'django.contrib.admin' 와
urls.py의 path('admin/', admin.site.urls) 을 주석처리하고 makemigrations 및 migrate를 해주면 된다고 적혀있는데..
해보니까 역시나 잘 안된다.