Skip to content

Commit

Permalink
Merge pull request #175 from weaverse-techtide/dev
Browse files Browse the repository at this point in the history
10월 13일
  • Loading branch information
AlbertImKr authored Oct 13, 2024
2 parents c80decf + 4e0586c commit ffaaaac
Show file tree
Hide file tree
Showing 77 changed files with 5,911 additions and 886 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
python manage.py migrate
- name: 테스트 실행
run: |
python manage.py test
pytest
94 changes: 93 additions & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,97 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import CustomUser

admin.site.register(CustomUser)

@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
"""
관리자로 하여금 커스텀 사용자 모델을 관리하기 위해 제공되는 관리자 클래스입니다.
- 모델로 CustomUser를 사용합니다.
- 커스텀 사용자 수정 및 생성 폼을 제공합니다.
- 커스텀 사용자 목록을 조회할 수 있으며, 필터링 및 검색 옵션을 제공합니다.
- 커스텀 사용자 목록에 표시될 필드들과 정렬 기준을 제공합니다.
- 모든 유형의 커스텀 사용자을 생성하는 기능을 제공합니다.
"""

model = CustomUser

readonly_fields = ("created_at", "updated_at")

fieldsets = (
("Login info", {"fields": ("email", "nickname", "password")}),
("Personal info", {"fields": ("first_name", "last_name", "introduction")}),
(
"Permissions",
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
"classes": ("wide",),
},
),
(
"Important dates",
{
"fields": ("last_login", "created_at", "updated_at"),
"classes": ("wide",),
},
),
)

add_fieldsets = (
(
"Register info",
{
"fields": (
"email",
"nickname",
"password1",
"password2",
"is_staff",
"is_superuser",
),
},
),
)

list_display = ("email", "nickname", "is_staff", "is_superuser")
list_filter = ("is_staff", "is_superuser", "is_active")
search_fields = ("email", "nickname", "first_name", "last_name")
ordering = ("email", "created_at")

def save_model(self, request, obj, form, change):
if not change:
if form.cleaned_data.get("is_staff") and not form.cleaned_data.get(
"is_superuser"
):
CustomUser.objects.create_staff(
email=form.cleaned_data["email"],
password=form.cleaned_data["password1"],
nickname=form.cleaned_data["nickname"],
)
elif form.cleaned_data.get("is_superuser"):
CustomUser.objects.create_superuser(
email=form.cleaned_data["email"],
password=form.cleaned_data["password1"],
nickname=form.cleaned_data["nickname"],
)
else:
CustomUser.objects.create_user(
email=form.cleaned_data["email"],
password=form.cleaned_data["password1"],
nickname=form.cleaned_data["nickname"],
)
else:
obj.email = form.cleaned_data.get("email", obj.email)
obj.nickname = form.cleaned_data.get("nickname", obj.nickname)
if "password1" in form.cleaned_data:
obj.set_password(form.cleaned_data["password1"])
obj.is_staff = form.cleaned_data.get("is_staff", obj.is_staff)
obj.is_superuser = form.cleaned_data.get("is_superuser", obj.is_superuser)
obj.save()
32 changes: 32 additions & 0 deletions accounts/migrations/0004_remove_customuser_date_joined_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.1.1 on 2024-10-04 06:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0003_customuser_created_at_customuser_introduction_and_more'),
]

operations = [
migrations.RemoveField(
model_name='customuser',
name='date_joined',
),
migrations.AlterField(
model_name='customuser',
name='introduction',
field=models.TextField(blank=True, max_length=20, null=True, verbose_name='자기소개'),
),
migrations.AlterField(
model_name='customuser',
name='nickname',
field=models.CharField(max_length=20, unique=True, verbose_name='닉네임'),
),
migrations.AlterField(
model_name='customuser',
name='phone_number',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='연락처'),
),
]
18 changes: 18 additions & 0 deletions accounts/migrations/0005_customuser_profile_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2024-10-10 06:29

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0004_remove_customuser_date_joined_and_more'),
]

operations = [
migrations.AddField(
model_name='customuser',
name='profile_image',
field=models.ImageField(blank=True, default='profile_images/default.jpg', upload_to='profile_images/'),
),
]
17 changes: 17 additions & 0 deletions accounts/migrations/0006_remove_customuser_profile_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.1 on 2024-10-10 07:31

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('accounts', '0005_customuser_profile_image'),
]

operations = [
migrations.RemoveField(
model_name='customuser',
name='profile_image',
),
]
75 changes: 44 additions & 31 deletions accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
from django.apps import apps
from django.contrib import auth
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ValidationError
from django.db import models


Expand All @@ -12,41 +8,51 @@ class CustomUserManager(UserManager):
- CustomUser에서 설정변경한 사용한 식별자(email)로 사용자 인스턴스를 생성하도록 합니다.
"""

def _create_user(self, email, password, **extra_fields):
def _create_user(self, email, password, nickname, **extra_fields):
"""
사용자 유형과 관계없이 실제로 사용자를 생성해 넘겨줍니다.
사용자 유형과 관계없이 사용자를 실제로 생성하고 반환합니다.
"""
if not email:
raise ValueError("이메일 입력은 필수입니다.")
raise ValueError("이메일은 필수로 입력하셔야 합니다.")
if not nickname:
raise ValueError("닉네임은 필수로 입력하셔야 합니다.")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user = self.model(email=email, nickname=nickname, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user

def create_user(self, email, password=None, **extra_fields):
def create_user(self, email, password, nickname, **extra_fields):
"""
일반 사용자를 생성합니다.
학생(student)를 생성합니다.
"""
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
return self._create_user(email, password, nickname, **extra_fields)

def create_superuser(self, email, password=None, **extra_fields):
def create_staff(self, email, password, nickname, **extra_fields):
"""
슈퍼 사용자를 생성합니다.
스태프(tutor)를 생성합니다.
"""
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self._create_user(email, password, **extra_fields)
extra_fields.setdefault("is_superuser", False)

if extra_fields.get("is_staff") is not True:
raise ValueError("Staff user must have is_staff=True.")
return self._create_user(email, password, nickname, **extra_fields)

def create_staff_user(self, email, password=None, **extra_fields):
def create_superuser(self, email, password, nickname, **extra_fields):
"""
관리자(강사)를 생성합니다.
관리자(superuser)를 생성합니다.
"""
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
extra_fields.setdefault("is_superuser", True)

if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, nickname, **extra_fields)


class CustomUser(AbstractUser):
Expand All @@ -56,14 +62,20 @@ class CustomUser(AbstractUser):
- 사용자 매니저 지정(UserManager -> CustomUserManager)
"""

# 미사용할 기본 필드
username = None
email = models.EmailField(
unique=True, verbose_name="이메일"
) # 필드를 기본키로 지정
date_joined = None

email = models.EmailField(unique=True, verbose_name="이메일") # 기본키 변경

nickname = models.CharField(max_length=20, verbose_name="닉네임")
phone_number = models.CharField(max_length=20, verbose_name="연락처")
introduction = models.TextField(max_length=20, verbose_name="자기소개")
# 추가 필드
nickname = models.CharField(max_length=20, unique=True, verbose_name="닉네임")
phone_number = models.CharField(
max_length=20, null=True, blank=True, verbose_name="연락처"
)
introduction = models.TextField(
max_length=20, null=True, blank=True, verbose_name="자기소개"
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="생성 일자")
updated_at = models.DateTimeField(auto_now=True, verbose_name="갱신 일자")

Expand All @@ -77,15 +89,16 @@ class CustomUser(AbstractUser):
verbose_name="수강 학생들", # 사용자에게 보일 이름
)

USERNAME_FIELD = "email" # 인증시 사용할 필드 지정
REQUIRED_FIELDS = []
# 인증시 사용할 필드
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["nickname"] # email, password 자동 포함

objects = CustomUserManager()

def __str__(self):
return self.email

def clean(self):
super().clean()
if not self.email:
raise ValidationError("이메일은 필수입니다.")
def get_image_url(self):
if getattr(self, "image", None):
return self.image.image_url
return "https://paullab.co.kr/images/weniv-licat.png"
43 changes: 6 additions & 37 deletions accounts/permissions.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
from rest_framework import permissions


class BaseAuthPermission(permissions.IsAuthenticated):
class IsAuthenticatedAndActive(permissions.IsAuthenticated):
"""
권한 값으로 True/False를 반환합니다.
- 사용자 객체 생성 및 인증 여부를 확인하고 인증자에게만 허용합니다.
- 사용자 객체 생성 및 인증 여부를 확인하고
active한 인증자에게만 허용합니다.
"""

message = "이 작업을 수행할 권한이 없습니다."


class IsAuthenticatedOrCreateOnly(BaseAuthPermission):
"""
권한 값으로 True/False를 반환합니다.
- GET 요청을 인증된 사용자에게 허용합니다.
- POST 요청을 누구에게나 허용합니다.
"""

message = "이 작업을 수행하려면 로그인이 필요합니다."

def has_permission(self, request, view):
if request.method == "GET":
return super().has_permission(request, view)
elif request.method == "POST":
return True
return False
return super().has_permission(request, view) and request.user.is_active


class IsTutor(BaseAuthPermission):
class IsTutor(IsAuthenticatedAndActive):
"""
권한 값으로 True/False를 반환합니다.
- 요청 유형과 관계없이 강사(Tutor)이면 권한을 허용합니다.
Expand All @@ -37,29 +24,11 @@ def has_permission(self, request, view):
return super().has_permission(request, view) and request.user.is_staff


class IsSuperUser(BaseAuthPermission):
class IsSuperUser(IsAuthenticatedAndActive):
"""
권한 값으로 True/False를 반환합니다.
- 요청 유형과 관계없이 관리자(superuser)이면 권한을 허용합니다.
"""

def has_permission(self, request, view):
return super().has_permission(request, view) and request.user.is_superuser


class IsTutorOrSuperUserOrSuperUserCreateOnly(BaseAuthPermission):
"""
권한 값으로 True/False를 반환합니다.
- GET 요청을 강사(tutor) 또는 관리자(superuser)에게 허용합니다.
- POST 요청을 관리자(superuser)에게만 허용합니다.
"""

def has_permission(self, request, view):
if not super().has_permission(request, view):
return False

if request.method == "GET":
return request.user.is_staff or request.user.is_superuser
elif request.method == "POST":
return request.user.is_superuser
return False
Loading

0 comments on commit ffaaaac

Please sign in to comment.