본문 바로가기
부트캠프TIL, WIL/AI웹개발(스파르타코딩클럽)

[AI웹개발][56일차TIL] Django 에서 admin 관리자 폼 커스터 마이징

by 우지uz 2023. 6. 3.

https://docs.djangoproject.com/ko/4.2/intro/tutorial07/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

저의 유저 모델과 유저 모델을 관리하는 매니저를 어드민 페이지에 등록하기 위해 
다음과 같이 작업 하였습니다. 

1. user/models.py 에 유저와 유저매니저 클래스 - 제가 필요한 필드값들을 직접 만들었습니다. 

2. user/forms.py 에 유저를 생성할때와 수정할 때 클래스 - 공식 문서를 참고해서, 가져왔습니다. 

3. user/admin.py 에 유저 어드민 페이지에 등록하기 - 필요한 것들을 커스터 마이징 해보았습니다. 


 

user/models.py에 유저와 유저매니저 클래스를 작성해줍니다. 

유저매니저 모델은
일반 유저와 슈퍼유저를 생성할 때 필요한 것들을 작성해주고

유저 모델은
유저에게 필요한 필드들과 조건들, 유저를 생성할 때 필요한
계정, 비밀번호, 필요한 필드들을 설정해줍니다.

(Django 인증 커스터마이징 하기, 공식 문서를 참고했습니다. )

from django.db import models
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import AbstractUser



class UserManager(BaseUserManager):
    # 유저를 생성하는 함수
    def create_user(self, email, account, username, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')
        # 유저를 생성할 때, 입력해야 하는 값들 + 비밀번호는 무조건 입력해야함
        user = self.model(
            email=self.normalize_email(email),
            account=account,
            username=username,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user
        
    # 슈퍼 유저를 생성하는 함수. python3 manage.py createsuperuser 할때 
    def create_superuser(self, email, account, username, password=None):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        # 슈퍼 유저를 생성할 때, 입력해야 하는 값들 + 비밀번호는 무조건 입력해야함
        user = self.create_user(
            email,
            password=password,
            account=account,
            username=username,
        )
        user.is_admin = True # 슈퍼 유저는 관리자 권한이 있음
        user.save(using=self._db)
        return user


class User(AbstractUser):
    # 메타 클래스는, DB 정보들에 대한 정보를 입력하는 곳
    class Meta:
        db_table = "my_user" # DB 테이블 이름을 my_user 로 설정해줌

    email = models.EmailField(
        verbose_name='이메일',
        max_length=255,
        unique=True,
    )
    GENDERS = (
        ('Men', 'Men'),
        ('Women', 'Women'),
    )
    # Email , account 는 unique 해야 한다.
    account = models.CharField("계정이름", null=False, max_length=50, unique=True)
    age = models.PositiveIntegerField("나이", null=True)
    username = models.CharField("유저이름", null=False, blank=False, max_length=50)
    gender = models.CharField("성별", choices=GENDERS, max_length=10)
    introduction = models.TextField("자기소개", null=True, blank=True)
    profile_img = models.ImageField(
        "프로필 이미지",
        upload_to='users/%Y%m%d',
        # height_field=None,
        # width_field=None,
        # max_length=None,
        # default='static/img/die1_1.png',  # default 이미지
        # default='default/die1_1.png',  # default 이미지
        blank=True,
    )
    followings = models.ManyToManyField(
        "self", symmetrical=False, related_name='followers', blank=True)
        # symmetrical 대칭 여부 False, 팔로우와 팔로워가 필요충분은 아님
    created_at = models.DateField("계정 생성일", auto_now_add=True)
    is_active = models.BooleanField("활성화 여부", default=True)
    is_admin = models.BooleanField("관리자 여부", default=False)

    objects = UserManager() #쿼리셋 매니저가 UserManager임을 밝힘
    # USERNAME_FIELD 와 REQUIRED_FIELDS는 유저를 생성할 때, 필요한 필드이기 때문에 create_user 및 create_superuser시 필드를 추가시켜 줘야 함
    USERNAME_FIELD = 'account' # 회원가입시, 계정이름으로 가입하기 때문에, Unique=True 로 해주어야 하는 필드
    REQUIRED_FIELDS = ['email', 'username',]


    def __str__(self):
        return str(self.username)

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin

user/forms.py 에 

유저를 생성할 때 폼 형식과 유저 정보를 수정할 때 폼 형식을
커스터 마이징 해줍니다. 

from django import forms
from django.core.exceptions import ValidationError
from .models import User
from django.contrib.auth.forms import ReadOnlyPasswordHashField



class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""

    password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
    password2 = forms.CharField(
        label="Password confirmation", widget=forms.PasswordInput
    )

    class Meta:
        model = User
        fields = ["email"]

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    disabled password hash display field.
    """

    password = ReadOnlyPasswordHashField()

    class Meta:
        model = User
        fields = ["password", "is_active", "is_admin"]

user/admins.py 에 

admin 페이지에 

from django.contrib import admin
from .models import User
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from user.forms import UserCreationForm, UserChangeForm


# Register your models here.
# Now register the new UserAdmin...


class MyUserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ["id", "account", "email", "username",
                     "age", "gender","is_active", "is_admin", "created_at",]
    list_filter = ["is_active", "is_admin"]
    fieldsets = [
        ("User Information", {"fields": ["account", "username", "age", "gender", "password", "followings", ]}),
        ("Permissions", {"fields": ["is_active", "is_admin"]}),
    ]
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = [
        (
            None,
            {
                "classes": ["wide"],
                "fields": ["account", "email", "password1", "password2"],
            },
        ),
    ]
    search_fields = ["account"]
    ordering = ["id"]
    filter_horizontal = []

admin.site.register(User, MyUserAdmin)

에 대한 자세한 설명을 드리겠습니다. 


form = UserChangeForm
add_form = UserCreationForm

말 그대로, 유저 수정 폼과 유저 생성 폼을 설정해줍니다. 


 

list_display
list_display = ["id", "account", "email", "username",
         		"age", "gender","is_active", "is_admin", "created_at",]

list_display 는 admin 페이지에서 user 에 들어갔을 때
유저 정보들을 보여주는 row를 의미합니다. 사진으로 보여드리겠습니다. 

list_filter
list_filter = ["is_active", "is_admin"]

list_filter 는 오른쪽 칸에 필터칸을 의미합니다.

활성화 여부와, 관리자 여부를 필터할 수 있습니다. 

 

fieldsets = [
        ("User Information", {"fields": ["account", "username", "age", "gender", "password", "followings", ]}),
        ("Permissions", {"fields": ["is_active", "is_admin"]}),
]

fieldsets은 각각의 유저들에 대한 정보들을
어떻게 보여줄 지에 대한 설정인데 

여기서는 크게 User Information과 Permissions라는 카테고리로 나눠서
필드들을 보여주고 있습니다.

사진은 다음과 같습니다. 

models.py 에 각 필드들에 대한 이름들을

email = models.EmailField(
        verbose_name='이메일',
        max_length=255,
        unique=True,
)
GENDERS = (
    ('Men', 'Men'),
    ('Women', 'Women'),
)
# Email , account 는 unique 해야 한다.
account = models.CharField("계정이름", null=False, max_length=50, unique=True)
age = models.PositiveIntegerField("나이", null=True)
username = models.CharField("유저이름", null=False, blank=False, max_length=50)
gender = models.CharField("성별", choices=GENDERS, max_length=10)
introduction = models.TextField("자기소개", null=True, blank=True)
profile_img = models.ImageField(
    "프로필 이미지",
    upload_to='users/%Y%m%d',
    # height_field=None,
    # width_field=None,
    # max_length=None,
    # default='static/img/die1_1.png',  # default 이미지
    # default='default/die1_1.png',  # default 이미지
    blank=True,
)
followings = models.ManyToManyField(
    "self", symmetrical=False, related_name='followers', blank=True)
    # symmetrical 대칭 여부 False, 팔로우와 팔로워가 필요충분은 아님
created_at = models.DateField("계정 생성일", auto_now_add=True)
is_active = models.BooleanField("활성화 여부", default=True)
is_admin = models.BooleanField("관리자 여부", default=False)

다음과 같이 저장해두었기 때문에, admin페이지에서도 변경된 이름으로 나오게 됩니다.


add_fieldsets
add_fieldsets = [
    (
        None,
        {
            "classes": ["wide"],
            "fields": ["account", "email", "password1", "password2"],
        },
    ),
]

"classes": ["wide"],의 의미는 어떤 것인지 잘 모르겠지만

여기서 fields의 값들이 다음과 같이 4개의 필드가 있기 때문에
유저를 생성할 때, 4개의 필드를 입력해야 함을 
다음 사진으로 보아 알 수 있습니다.

 


search_fields
search_fields = ["account"]

search_fields는 유저를 검색할 때, 원하는 필드를 설정해주는 값입니다.
현재는 account로 설정되어 있기 때문에 
계정 이름에 대한 검색을 할 수 있게 됩니다. 

popk를 검색하게 되면

다음과 같이 두 유저의 정보들을 모아 볼 수 있습니다. 


 

ordering

ordering은 정리하는 순서를 의미합니다.

ordering = ["id"]

지금은 id 를 기준으로, 순서를 정했지만

ordering = ["account"]

account를 기준으로 정렬하면

알파벳 순서로 유저들이 정렬됩니다. 


마지막으로 

filter_horizontal = []

admin.site.register(User, MyUserAdmin)

filter_horizontal = [] 은 아직 써보지 못해서, 어떤 코드인지 잘 모르겠고

admin.site.register(User, MyUserAdmin) 은
장고 어드민 페이지에, User 모델과 유저모델을 관리하는 MyUserAdmin를 등록하는 것입니다.

 

추후 다른 모델들도 추가해서, 어드민 페이지를 통해 백 오피스 환경을 만들어보려 합니다. 

깃 허브 링크도 내일 추가하겠습니다.