Django 커스텀 유저 모델

.·2021년 4월 8일
0
post-custom-banner

개요

장고는 기본적으로 User Model 을 제공한다.
하지만 기본적으로 제공되는 필드들로만은 원하는 서비스를 만들지 못한다.
주소 , 전화번호 , 프로필 사진 등을 등록할려면 Custom User Model 을 사용해야 한다.
이번 포스트는 장고 커스텀 유저모델을 사용하는 방법에 대해서 알아보도록 한다.


Django 프로젝트 준비

프로젝트 생성

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)는 두가지 함수를 가지고 있다.

  • create_user(username_field, password=None, **other_fields)
  • create_superuser(username_field, password, **other_fields)

첫번째 파라미터가 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)로 사용하기 위해서는 구현해야 하는 부분이다.

  • def has_perm(self, perm, obj=None):: True를 반환하여 권한이 있음을 알립니다. Ojbect를 반환하는 경우 해당 Object로 사용 권한을 확인하는 절차가 필요하다.
  • def has_module_perms(self, app_label):: True를 반환하여 주어진 앱(App)의 모델(Model)에 접근 가능하도록 한다.
  • def is_staff(self):: True가 반환되면 장고(django)의 관리자 화면에 로그인 할 수 있습니다.

관리자 페이지 수정

장고의 관리자 페이지 사용하는 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 은 사용하지 않도록 설정한다.


Setting.py 커스텀 유저 모델 등록

우리가 만든 커스텀 유저 모델을 인증 모델(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를 해주면 된다고 적혀있는데..

해보니까 역시나 잘 안된다.

profile
.
post-custom-banner

0개의 댓글