"회원관리 2. 커스텀유저(AbstractUser 이용)"의 두 판 사이의 차이
(→모델 작성) |
|||
(같은 사용자의 중간 판 7개는 보이지 않습니다) | |||
1번째 줄: | 1번째 줄: | ||
[[분류:장고 기능구현(초급)]] | [[분류:장고 기능구현(초급)]] | ||
==개요== | ==개요== | ||
− | |||
− | 장고에서 기본적으로 제공해주는 항목을 넘어 개별항목을 지정하는 등의 작업을 하기 위해 처음부터 커스텀유저를 사용하길 권장한다. | + | === 시작에 앞서... === |
+ | # 프로젝트의 시작 전에 구현해야 한다. | ||
+ | #:- 기존에 가입된 회원들이 있는 상태에서 새로운 DB 테이블을 정의할 때, migrate 따위의 명령을 실행하면 <code>django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency 앱이름.0001_initial on database 'default'.</code> 같은 에러가 뜬다. 가장 간단한 해결법은 기존 DB를 삭제하고 다시 처음부터 설계를 반영하는 것이다. 때문에 이런 불상사를 막으려면 프로젝트를 제대로 시작하기 전에 최소한의 앱과 커스텀 유저는 구현해 두어야 한다. | ||
+ | #:- 만약 프로젝트의 중간에 추가해 넣어야 한다면 다음의 문서를 참고하자. [https://code.djangoproject.com/ticket/25313 링크.] 개빡센 여정이 예상된다. | ||
+ | |||
+ | * 기본적으로 제공되는 유저모델은 제한적이다. 학교에서 운영한다면 학번 등의 정보를 저정해야 하는데, 이처럼 제공하지 않는 기능들을 확장하는 과정이 필요하다. | ||
+ | * 장고에서 기본적으로 제공해주는 항목을 넘어 개별항목을 지정하는 등의 작업을 하기 위해 처음부터 커스텀유저를 사용하길 권장한다. | ||
+ | * 일반적으로 본격적인 프로젝트를 시작하기 전에 만들기가 권장된다. 프로젝트 도중에 모델을 바꾸는 것은 DB의 왜래키나 다대다 관계에 영향을 미치는데, 이를 다시 고치는 일은 꽤 어려운 일이기 때문이다.(중간에 커스텀모델을 추가하는 경우엔 DB자체를 지우고, 다시 구성한다.) 장고의 의존성 특성 때문에 최초의 DB에 생성되어 포함되어야 한다. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+유저 기능을 확장하는 방법<ref>https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html</ref> | |+유저 기능을 확장하는 방법<ref>https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html</ref> | ||
27번째 줄: | 33번째 줄: | ||
기본 속성으로 id(기본키), password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined 필드가 들어있다. | 기본 속성으로 id(기본키), password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined 필드가 들어있다. | ||
− | |} | + | |} |
===사용하는 것은 AbstractBaseUser=== | ===사용하는 것은 AbstractBaseUser=== | ||
AbstractUser는 크게 변경하거나 확장하는 내용이 많지 않다.(그냥 Profile 방식을 사용해도 되지 않을까 싶을 정도로..) 하여, 여기에선 조금 어렵지만, 더 자유로운 BaseUser에 대해 다뤄보고자 한다. | AbstractUser는 크게 변경하거나 확장하는 내용이 많지 않다.(그냥 Profile 방식을 사용해도 되지 않을까 싶을 정도로..) 하여, 여기에선 조금 어렵지만, 더 자유로운 BaseUser에 대해 다뤄보고자 한다. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==사전작업== | ==사전작업== | ||
− | 어떤 앱의 어떤 모델을 기본 유저로 사용할지 생각하고 진행한다. | + | 어떤 앱의 어떤 모델을 기본 유저로 사용할지 생각하고 진행한다.(그냥 앱을 새로 파는 게 적절하다.) |
==모델 작성== | ==모델 작성== | ||
− | |||
− | 사람마다 짜는 방식이 달라서, 그리고 일일이 주석을 달아두지 않기 때문에 처음 하는 사람들은 해석하는 데 적잖이 애먹는다. | + | * 필수적으로 2개의 모델을 작성해주어야 한다. BaseUserManager를 상속받은 모델과 AbstractBaseUser를 상속받은 모델. |
+ | * 사람마다 짜는 방식이 달라서, 그리고 일일이 주석을 달아두지 않기 때문에 처음 하는 사람들은 해석하는 데 적잖이 애먹는다. | ||
+ | * 아래에선 계정 식별자를 identifier로 설정했는데, 장고에서 기본적으로 사용하는 username 필드를 사용하는 편이 좋다.(권한 관련 확장기능에서 username을 그대로 사용하는 경우가 많아, 계정 식별자는 username을 사용하는 편이 좋다.) | ||
+ | |||
{| class="wikitable" | {| class="wikitable" | ||
!과정 | !과정 | ||
52번째 줄: | 54번째 줄: | ||
|<syntaxhighlight lang="python"> | |<syntaxhighlight lang="python"> | ||
from django.db import models | from django.db import models | ||
− | from django.contrib.auth.models import | + | from django.contrib.auth.models import BaseUserManager, AbstractBaseUser |
</syntaxhighlight> | </syntaxhighlight> | ||
|- | |- | ||
58번째 줄: | 60번째 줄: | ||
|유저를 생성할 때 사용하는 헬퍼. | |유저를 생성할 때 사용하는 헬퍼. | ||
AbstractBaseUser가 참고하니, 그보다 위에 작성되어야 한다. | AbstractBaseUser가 참고하니, 그보다 위에 작성되어야 한다. | ||
+ | |||
인수로 받는 email 등을 다른 것으로 바꿀 수 있다. | 인수로 받는 email 등을 다른 것으로 바꿀 수 있다. | ||
+ | |||
166번째 줄: | 170번째 줄: | ||
===DB에 반영=== | ===DB에 반영=== | ||
− | 이 작업을 처음에 하는 게 아니라, 개발 중에 하는 경우, 다른 앱의 모델에서 유저를 참조하고 있을 때 | + | 이 작업을 처음에 하는 게 아니라, 개발 중에 하는 경우, 다른 앱의 모델에서 유저를 참조하고 있을 때 참조 테이블로 User가 들어가 있는데, 이 때문에 'auth.User', which has been swapped out. 라는 에러가 뜬다. |
이것과 from django.contrib.auth.models import User를 지우고, from django.conf import settings 후에 | 이것과 from django.contrib.auth.models import User를 지우고, from django.conf import settings 후에 | ||
− | User 대신 settings. | + | User 대신 <code>settings.AUTH_USER_MODEL</code>를 넣어주면 된다. |
+ | |||
+ | 위 <code>settings.AUTH_USER_MODEL</code>은 텍스트만 반환하기 때문에, 유저 객체 자체를 불러오고 싶다면 <code>from django.contrib.auth import get_user_model</code> 을 사용하면 된다. | ||
===settings.py에 만들 모델 추가=== | ===settings.py에 만들 모델 추가=== | ||
− | 아래 작업은 DB 반영이 끝나 후에 진행해야 한다. 그렇지 않으면 ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'account.user', but app 'account' isn't installed. 따위의 에러가 발생한다. | + | 아래 작업은 DB 반영이 끝나 후에 진행해야 한다. 그렇지 않으면 <code>ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'account.user', but app 'account' isn't installed.</code> 따위의 에러가 발생한다. |
− | + | 다음과 같은 줄을 settings.py 내에 추가한다. | |
− | |||
+ | 보통은 앱이름은 custom_account, 모델 이름은 User 따위가 된다.<syntaxhighlight lang="python"> | ||
+ | AUTH_USER_MODEL = 'custom_account.User' # 관리유저로 사용할 모델을 설정한다. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== 중간 확인 === | === 중간 확인 === | ||
− | * python manage.py createsuperuser로 | + | * python manage.py createsuperuser로 관리 계정 생성. |
* 사이트를 실행시킨 후 /admin에 들어가 로그인해본다. | * 사이트를 실행시킨 후 /admin에 들어가 로그인해본다. | ||
==관리자 화면에 등록== | ==관리자 화면에 등록== | ||
− | admin.py를 작성한다.<syntaxhighlight lang="python"> | + | 아직 관리자 화면에선 가입한 유저들의 정보가 나오지 않는다. 이들을 한눈에 관리하기 위해 admin.py를 작성한다.<syntaxhighlight lang="python"> |
from django.contrib import admin | from django.contrib import admin | ||
from .models import User # 직접 등록한 모델 | from .models import User # 직접 등록한 모델 | ||
193번째 줄: | 200번째 줄: | ||
list_display = ('identifier',) | list_display = ('identifier',) | ||
exclude = ('password',) # 사용자 상세 정보에서 비밀번호 필드를 노출하지 않음 | exclude = ('password',) # 사용자 상세 정보에서 비밀번호 필드를 노출하지 않음 | ||
− | </syntaxhighlight>그럼 이제 이 모델로 | + | </syntaxhighlight>그럼 이제 이 모델로 계정 관리가 가능해진다. |
==폼 작성== | ==폼 작성== |
2022년 12월 15일 (목) 13:38 기준 최신판
1 개요[편집 | 원본 편집]
1.1 시작에 앞서...[편집 | 원본 편집]
- 프로젝트의 시작 전에 구현해야 한다.
- - 기존에 가입된 회원들이 있는 상태에서 새로운 DB 테이블을 정의할 때, migrate 따위의 명령을 실행하면
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency 앱이름.0001_initial on database 'default'.
같은 에러가 뜬다. 가장 간단한 해결법은 기존 DB를 삭제하고 다시 처음부터 설계를 반영하는 것이다. 때문에 이런 불상사를 막으려면 프로젝트를 제대로 시작하기 전에 최소한의 앱과 커스텀 유저는 구현해 두어야 한다. - - 만약 프로젝트의 중간에 추가해 넣어야 한다면 다음의 문서를 참고하자. 링크. 개빡센 여정이 예상된다.
- - 기존에 가입된 회원들이 있는 상태에서 새로운 DB 테이블을 정의할 때, migrate 따위의 명령을 실행하면
- 기본적으로 제공되는 유저모델은 제한적이다. 학교에서 운영한다면 학번 등의 정보를 저정해야 하는데, 이처럼 제공하지 않는 기능들을 확장하는 과정이 필요하다.
- 장고에서 기본적으로 제공해주는 항목을 넘어 개별항목을 지정하는 등의 작업을 하기 위해 처음부터 커스텀유저를 사용하길 권장한다.
- 일반적으로 본격적인 프로젝트를 시작하기 전에 만들기가 권장된다. 프로젝트 도중에 모델을 바꾸는 것은 DB의 왜래키나 다대다 관계에 영향을 미치는데, 이를 다시 고치는 일은 꽤 어려운 일이기 때문이다.(중간에 커스텀모델을 추가하는 경우엔 DB자체를 지우고, 다시 구성한다.) 장고의 의존성 특성 때문에 최초의 DB에 생성되어 포함되어야 한다.
방법 | 설명 |
---|---|
Proxy. 대리모델 사용하기. | 장고 내에서가 아닌, 다른 사이트에서 사용되는 유저권한 등을 위임받아와 사용한다. |
Profile. 프로필 사용. | 단순히 프로필에 들어갈 내용을 더 확장하려면 새로운 모델을 만들어 OneToOneField를 사용해 프로필을 늘리면 되는데, 굳이 어려운 커스텀모델을 다시 짜는 이유는 아이디 대신 이메일로 로그인한다든가, 권한을 부여하는 등 다양한 편의기능을 구현하기 위함이다. 게다가 이런 방식으로 만들면 또 다른 DB를 불러와야 한다는 단점도 있다.
굳이 편의기능이 필요하지 않다면 Admin창에 InlineModelAdmin 등으로 모델을 연결하여 관리하면 된다. 그러나, 공부를 위해, 이후 추가될 기능들을 위해 미리 익혀두면 좋으리라. |
AbstractBaseUser. | 완전 새로운 모델을 사용하기 위함.(모델을 상속해서 만들어진다.) DB에 큰 영향을 미치기 때문에 프로젝트 시작 전에 구성되어야 한다.
id 대신 이메일 로그인을 구현한다든가 특수한 기능이 필요할 때 사용한다.(장고에서 제공하는 권한이 프로젝트와 맞지 않은 경우) 기본 속성으로 id(기본키), password, last_login 필드만 들어있다. |
AbstractUser. | 존재하는 기존 필드들을 사용한다.(위 모델의 확장판이라 보면 된다.) 이 모델을 사용한다 해서 이메일 로그인을 구현하지 못하는 것은 아니다. 마찬가지로 프로젝트 시작 전에 구성되어야 한다.
사용하기 위해선 settings.py의 설정이 필요하다. 기본 속성으로 id(기본키), password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined 필드가 들어있다. |
1.2 사용하는 것은 AbstractBaseUser[편집 | 원본 편집]
AbstractUser는 크게 변경하거나 확장하는 내용이 많지 않다.(그냥 Profile 방식을 사용해도 되지 않을까 싶을 정도로..) 하여, 여기에선 조금 어렵지만, 더 자유로운 BaseUser에 대해 다뤄보고자 한다.
2 사전작업[편집 | 원본 편집]
어떤 앱의 어떤 모델을 기본 유저로 사용할지 생각하고 진행한다.(그냥 앱을 새로 파는 게 적절하다.)
3 모델 작성[편집 | 원본 편집]
- 필수적으로 2개의 모델을 작성해주어야 한다. BaseUserManager를 상속받은 모델과 AbstractBaseUser를 상속받은 모델.
- 사람마다 짜는 방식이 달라서, 그리고 일일이 주석을 달아두지 않기 때문에 처음 하는 사람들은 해석하는 데 적잖이 애먹는다.
- 아래에선 계정 식별자를 identifier로 설정했는데, 장고에서 기본적으로 사용하는 username 필드를 사용하는 편이 좋다.(권한 관련 확장기능에서 username을 그대로 사용하는 경우가 많아, 계정 식별자는 username을 사용하는 편이 좋다.)
과정 | 설명 | 코드 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
모듈 임포트 | 모델과 유저모델을 불러온다. | from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
| ||||||||||||||||||
BaseUserManager | 유저를 생성할 때 사용하는 헬퍼.
AbstractBaseUser가 참고하니, 그보다 위에 작성되어야 한다.
인수로 받는 email 등을 다른 것으로 바꿀 수 있다.
다음 코드는 email 대신 identifier를 넣는 등 메뉴얼에서 제공하는 코드를 바꾸어, 최대한 단순화하여 아이디와 패스워드만 받는 코드이다. |
class UserManager(BaseUserManager):
def create_user(self, identifier, password=None):
user = self.model( # 이 안에 유저 정보에 필요한 필드를 넣으면 된다.
identifier = identifier # 왼쪽이 필드, 오른쪽이 넣을 값.
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, identifier, password):
user = self.create_user(
identifier,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
| ||||||||||||||||||
AbstractBaseUser | 커스텀 유저 모델. 다음 코드도 최대한 단순화 한 것이지만... 필수 필드를 갖고 있다.
필수인듯 필수 아닌 기타 필드 꼭 정의되지 않아도 되지만, 정의되지 않으면 admin 페이지 접속 자체가 안된다.
기타 필드
last_login 필드는 자동으로 생성된다. |
class User(AbstractBaseUser):
identifier = models.CharField(max_length=20, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserManager() # 회원가입을 다룰 클래스를 지정한다.
USERNAME_FIELD = 'identifier' # 식별자로 사용할 필드.
REQUIRED_FIELDS = [] # 회원가입 때 필수 입력필드.
def __str__(self):
return self.identifier
def has_perm(self, perm, obj=None):
'''권한 소지여부를 판단하기 위한 메서드'''
return True
def has_module_perms(self, app_label):
'''앱 라벨을 받아, 해당 앱에 접근 가능한지 파악'''
return True
@property
def is_staff(self):
'''이게 True면 관리자화면에 접근 가능'''
return self.is_admin
| ||||||||||||||||||
3.1 DB에 반영[편집 | 원본 편집]
이 작업을 처음에 하는 게 아니라, 개발 중에 하는 경우, 다른 앱의 모델에서 유저를 참조하고 있을 때 참조 테이블로 User가 들어가 있는데, 이 때문에 'auth.User', which has been swapped out. 라는 에러가 뜬다.
이것과 from django.contrib.auth.models import User를 지우고, from django.conf import settings 후에
User 대신 settings.AUTH_USER_MODEL
를 넣어주면 된다.
위 settings.AUTH_USER_MODEL
은 텍스트만 반환하기 때문에, 유저 객체 자체를 불러오고 싶다면 from django.contrib.auth import get_user_model
을 사용하면 된다.
3.2 settings.py에 만들 모델 추가[편집 | 원본 편집]
아래 작업은 DB 반영이 끝나 후에 진행해야 한다. 그렇지 않으면 ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'account.user', but app 'account' isn't installed.
따위의 에러가 발생한다.
다음과 같은 줄을 settings.py 내에 추가한다.
보통은 앱이름은 custom_account, 모델 이름은 User 따위가 된다.
AUTH_USER_MODEL = 'custom_account.User' # 관리유저로 사용할 모델을 설정한다.
3.3 중간 확인[편집 | 원본 편집]
- python manage.py createsuperuser로 관리 계정 생성.
- 사이트를 실행시킨 후 /admin에 들어가 로그인해본다.
4 관리자 화면에 등록[편집 | 원본 편집]
아직 관리자 화면에선 가입한 유저들의 정보가 나오지 않는다. 이들을 한눈에 관리하기 위해 admin.py를 작성한다.
from django.contrib import admin
from .models import User # 직접 등록한 모델
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ('identifier',)
exclude = ('password',) # 사용자 상세 정보에서 비밀번호 필드를 노출하지 않음
그럼 이제 이 모델로 계정 관리가 가능해진다.
5 폼 작성[편집 | 원본 편집]
모델 작성은 끝났는데, 해당 정보를 기입하기 위한 폼이 필요하다. 회원가입 시에도 필요하고.
기본 폼은 password 따위를 암호화하지 않고 그대로 저장하기 때문이다. 폼을 밑바닥부터 만들려면 알아야 할 것도 많은데, 직접 만들기보단, UserCreationForm을 이용해 폼을 다뤄보자.
앱 안에 forms.py를 만들어 작성하자.
changeform은 굳이 사용하지 않아도 된다. 회원관리 참고..
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth import get_user_model # 직접 불러올 수도 있지만, settings 파일에서 설정된 모델을 자동으로 불러오는 편이 수정에 유용하지.
class User_create_form(UserCreationForm):
class Meta:
model = get_user_model() # 설정된 커스텀모델
fields = ["identifier"] # password1,2 필드는 자동추가된다.
# 아래 코드는 유저정보 수정을 위한 것으로, 없어도 무방하다.
class User_change_form(UserChangeForm):
class Meta:
model = get_user_model()
fields = ["identifier", "password1", "password2"]
password부분은 UserCreationForm에 정의된 것들이라 오버라이드 해줘야 한다.
비밀번호 조건은 settings.py의 AUTH_PASSWORD_VALIDATORS 안에 있는 조건들을 거쳐야 한다.
6 모델[편집 | 원본 편집]
커스텀유저를 외래키로 사용하고 싶다면
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
위와 같은 형태로 사용하면 된다.
[의문. 글쓰기 뷰에서 저자를 불러올 때에도 settings.AUTH_USER_MODEL이면 될까/?]