바뀜

11,528 바이트 추가됨 ,  2022년 12월 15일 (목) 13:38
편집 요약 없음
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"
 +
|+유저 기능을 확장하는 방법<ref>https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html</ref>
 +
!방법
 +
!설명
 +
|-
 +
|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 필드가 들어있다.
 +
|}
 +
===사용하는 것은 AbstractBaseUser===
 +
AbstractUser는 크게 변경하거나 확장하는 내용이 많지 않다.(그냥 Profile 방식을 사용해도 되지 않을까 싶을 정도로..) 하여, 여기에선 조금 어렵지만, 더 자유로운 BaseUser에 대해 다뤄보고자 한다.
 +
 
 +
==사전작업==
 +
어떤 앱의 어떤 모델을 기본 유저로 사용할지 생각하고 진행한다.(그냥 앱을 새로 파는 게 적절하다.)
 +
==모델 작성==
 +
 
 +
* 필수적으로 2개의 모델을 작성해주어야 한다. BaseUserManager를 상속받은 모델과 AbstractBaseUser를 상속받은 모델.
 +
* 사람마다 짜는 방식이 달라서, 그리고 일일이 주석을 달아두지 않기 때문에 처음 하는 사람들은 해석하는 데 적잖이 애먹는다.
 +
* 아래에선 계정 식별자를 identifier로 설정했는데, 장고에서 기본적으로 사용하는 username 필드를 사용하는 편이 좋다.(권한 관련 확장기능에서 username을 그대로 사용하는 경우가 많아, 계정 식별자는 username을 사용하는 편이 좋다.)
 +
 
 +
{| class="wikitable"
 +
!과정
 +
!설명
 +
!코드
 +
|-
 +
|모듈 임포트
 +
|모델과 유저모델을 불러온다.
 +
|<syntaxhighlight lang="python">
 +
from django.db import models
 +
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
 +
</syntaxhighlight>
 +
|-
 +
|BaseUserManager
 +
|유저를 생성할 때 사용하는 헬퍼.
 +
AbstractBaseUser가 참고하니, 그보다 위에 작성되어야 한다.
 +
 
 +
 
 +
 
 +
인수로 받는 email 등을 다른 것으로 바꿀 수 있다.
 +
 
 +
 
 +
 
 +
다음 코드는 email 대신 identifier를 넣는 등 메뉴얼에서 제공하는 코드를 바꾸어,
 +
 
 +
최대한 단순화하여 아이디와 패스워드만 받는 코드이다.
 +
|<syntaxhighlight lang="python">
 +
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
 +
</syntaxhighlight>
 +
|-
 +
|AbstractBaseUser
 +
|커스텀 유저 모델. 다음 코드도 최대한 단순화 한 것이지만... 필수 필드를 갖고 있다.
 +
{| class="wikitable"
 +
!필수필드
 +
!설명
 +
|-
 +
|is_active
 +
|계정을 활성화 하는가.
 +
|-
 +
|is_admin
 +
|관리자 계정인가.
 +
|}
 +
'''필수인듯 필수 아닌 기타 필드'''
 +
 
 +
꼭 정의되지 않아도 되지만, 정의되지 않으면 admin 페이지 접속 자체가 안된다.
 +
{| class="wikitable"
 +
!기타필드 및 메서드
 +
!설명
 +
|-
 +
|is_staff
 +
|어드민에 접속할 권한.
 +
|-
 +
|has_perm
 +
|<syntaxhighlight lang="python3">
 +
def has_perm(self, perm, obj=None):
 +
    '''권한 소지여부를 판단하기 위한 메서드'''
 +
    return True
 +
</syntaxhighlight>
 +
|-
 +
|has_module_perms
 +
|<syntaxhighlight lang="python3">
 +
def has_module_perms(self, app_label):
 +
    '''앱 라벨을 받아, 해당 앱에 접근 가능한지 파악'''
 +
    return True
 +
</syntaxhighlight>
 +
|}
 +
'''기타 필드'''
 +
{| class="wikitable"
 +
!기타필드
 +
!설명
 +
|-
 +
|is_superuser
 +
|그냥 모든 권한.
 +
|}
 +
last_login 필드는 자동으로 생성된다.
 +
|<syntaxhighlight lang="python">
 +
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
 +
</syntaxhighlight>
 +
|-
 +
|
 +
|
 +
|
 +
|}
 +
 
 +
===DB에 반영===
 +
이 작업을 처음에 하는 게 아니라, 개발 중에 하는 경우, 다른 앱의 모델에서 유저를 참조하고 있을 때 참조 테이블로 User가 들어가 있는데, 이 때문에 'auth.User', which has been swapped out. 라는 에러가 뜬다.
 +
 
 +
이것과 from django.contrib.auth.models import User를 지우고, from django.conf import 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에 만들 모델 추가===
 +
아래 작업은 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>
 +
 
 +
=== 중간 확인 ===
 +
 
 +
* python manage.py createsuperuser로 관리 계정 생성.
 +
* 사이트를 실행시킨 후 /admin에 들어가 로그인해본다.
 +
 
 +
==관리자 화면에 등록==
 +
아직 관리자 화면에선 가입한 유저들의 정보가 나오지 않는다. 이들을 한눈에 관리하기 위해 admin.py를 작성한다.<syntaxhighlight lang="python">
 +
from django.contrib import admin
 +
from .models import User  # 직접 등록한 모델
 +
 
 +
@admin.register(User)
 +
class UserAdmin(admin.ModelAdmin):
 +
    list_display = ('identifier',)
 +
    exclude = ('password',)  # 사용자 상세 정보에서 비밀번호 필드를 노출하지 않음
 +
</syntaxhighlight>그럼 이제 이 모델로 계정 관리가 가능해진다.
 +
 
 +
==폼 작성==
 +
모델 작성은 끝났는데, 해당 정보를 기입하기 위한 폼이 필요하다. 회원가입 시에도 필요하고.
 +
 
 +
기본 폼은 password 따위를 암호화하지 않고 그대로 저장하기 때문이다. 폼을 밑바닥부터 만들려면 알아야 것도 많은데, 직접 만들기보단, UserCreationForm을 이용해 폼을 다뤄보자.
 +
 
 +
앱 안에 forms.py를 만들어 작성하자.
 +
 
 +
changeform은 굳이 사용하지 않아도 된다. 회원관리 참고..<syntaxhighlight lang="python">
 +
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"]
 +
</syntaxhighlight>password부분은 UserCreationForm에 정의된 것들이라 오버라이드 해줘야 한다.
 +
 
 +
비밀번호 조건은 settings.py의 AUTH_PASSWORD_VALIDATORS 안에 있는 조건들을 거쳐야 한다.
 +
 
 +
 
 +
 
 +
==모델==
 +
커스텀유저를 외래키로 사용하고 싶다면
 +
 
 +
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
 +
 
 +
위와 같은 형태로 사용하면 된다.
 +
 
 +
[의문. 글쓰기 뷰에서 저자를 불러올 때에도 settings.AUTH_USER_MODEL이면 될까/?]
 +
=각주=
 +
<references />