account system - pai-plznw4me/django-initializer GitHub Wiki

계정 관리 μ‹œμŠ€ν…œ

계정을 관리 ν•˜λŠ” μ‹œμŠ€ν…œ μž…λ‹ˆλ‹€. ν•΄λ‹Ή λ³Έ λ¬Έμ„œλŠ” django 기반의 계정 관리 μ‹œμŠ€ν…œμ„ λΉ λ₯΄κ²Œ μ›Ή ν”„λ‘œκ·Έλž¨μ— μ μš©ν•˜κΈ° μœ„ν•΄ 개발 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
⚠️ ν•΄λ‹Ή λ¬Έμ„œλŠ” django account을 μ‚¬μš©ν•΄ κ°œλ°œμ„ ν•˜λŠ” 개발자용 λ¬Έμ„œμž…λ‹ˆλ‹€.
ν•΄λ‹Ή λ¬Έμ„œλ₯Ό 읽고 django 계정 관리 μ‹œμŠ€ν…œμ„ 본인이 μ§„ν–‰ν•˜κ³ μž ν•˜λŠ” ν”„λ‘œμ νŠΈμ— 적용 및 μ»€μŠ€ν…€λ§ˆμ΄μ§• ν• μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.
git page : account

λͺ©μ°¨

  1. λ“€μ–΄κ°€κΈ° 전에
  2. auth URL 등둝
  3. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ
  4. Login & Logout
  5. Profile
  6. Superuser
  7. Admin Site

0. λ“€μ–΄κ°€κΈ° 전에

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

  1. Project 생성 ( ν•΄λ‹Ή ν¬μŠ€νŒ…μ—μ„œλŠ” ams )
  2. app 생성( ν•΄λ‹Ή ν¬μŠ€νŒ…μ—μ„œλŠ” account)
  3. app 등둝
  4. app url 생성 및 등둝
  5. package install

1. auth url 등둝

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

django λ‚΄ κΈ°λ³Έ app 쀑 ν•˜λ‚˜μΈ auth app url 을 λ“±λ‘ν•œλ‹€.

# 파일 μœ„μΉ˜
# ams
# |-account
#   |-ams
#     |-urls.py <-- 파일 μœ„μΉ˜ 

urlpatterns = [
    path('admin/', admin.site.urls),
    path('account', include('account.urls'), name='account'),
    path('account/', include('django.contrib.auth.urls')),  #<-- django auth app url 을 등둝
]
  • μ„œλ²„ μ‹€ν–‰μ‹œ ν›„ http://127.0.0.1:8000/account/ μ‹€ν–‰μ‹œ μ•„λž˜μ™€ 같은 νŽ˜μ΄μ§€ 생성 되면 정상
    βœ… checkpoint account

2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

django μ„€μΉ˜μ‹œ κΈ°λ³Έ μ•± κΈ°λŠ₯으둜 μœ μ € λͺ¨λΈμ„ μ œκ³΅ν•œλ‹€.ν•˜μ§€λ§Œ μœ μ € 생성 κΈ°λŠ₯은 κ΄€λ¦¬μž μ•±μ—μ„œ μ œκ³΅λœλ‹€. ν•΄λ‹Ή νŽ˜μ΄μ§€μ—μ„œλŠ” django κΈ°λ³Έ μœ μ € λͺ¨λΈμ„ ν™œμš©ν•΄ νšŒμ› κ°€μž… νŽ˜μ΄μ§€λ₯Ό λ§Œλ“€κ³  κΈ°λ³Έ μœ μ € λͺ¨λΈμ„ ν™•μž₯ν•˜μ—¬ κΈ°μ‘΄ μœ μ € λͺ¨λΈμ— μƒˆλ‘œμš΄ ν•„λ“œλ₯Ό μΆ”κ°€ ν•  수 μžˆλ„λ‘ ν•œλ‹€.

  • Model - Form - View- Template νŒ¨ν„΄μœΌλ‘œ 개발 됨.

2.1. Custom Model 생성

  • User λͺ¨λΈ 상속받아 μƒˆλ‘œμš΄ field μΆ”κ°€ν•˜κΈ°
from django.contrib.auth.models import AbstractUser
from django.db import models


# Create your models here.
class CustomUser(AbstractUser): # <-- 상속 λΆ€λΆ„
    phone_number = models.CharField(max_length=11)  # μ „ν™”λ²ˆν˜Έ(char)
    career = models.IntegerField()  # κ²½λ ₯ (int)
    rank = models.CharField(max_length=10)  # 직급 (select)
    region = models.CharField(max_length=100)  # μ£Όμ†Œ
    date_company_joined = models.DateField()  # μž…μ‚¬ κΈ°κ°„
    id_number = models.CharField(max_length=13)  # μ£Όλ―Όλ“±λ‘λ²ˆν˜Έ
    department = models.CharField(max_length=13)  # λΆ€μ„œ
    resume = models.FileField(blank=True, null=True)  # 이λ ₯μ„œ
    id_photo = models.ImageField(blank=True, null=True, upload_to='')  # 증λͺ…사진


    def __str__(self):
        return self.username
  • AbstractUser 을 μƒμ†λ°›λŠ” CustomUser μ—μ„œλŠ” μœ„ AbstractUser μ—μ„œ μ •μ˜λœ field 이외에 μΆ”κ°€ ν•˜κ³  싢은 field 을 μ •μ˜ν•˜λ©΄ λœλ‹€

    • AbstractUser μ•„λž˜μ™€ 같은 field을 κ°€μ§€κ³  μžˆλ‹€.

      • username

      • first_name

      • last_name

      • email

      • date_joined

      • is_staff

  • ⚠️ μƒˆλ‘­κ²Œ μΆ”κ°€λ˜λŠ” ν•„λ“œμ— null ν—ˆμš©μ΄ μ•ˆλ˜κ±°λ‚˜ ν•„λ“œμ— 기본값이 μ„€μ •λ˜μ§€ μ•ŠμœΌλ©΄ manage.py createsupseruser 으둜 μŠˆνΌμœ μ €μƒμ„±μ‹œ admin site 에 접근이 μ•ˆλ μˆ˜ μžˆλ‹€.

2.2 Signup ModelForm 생성

class CustomUserCreationForm(UserCreationForm):
    class Meta:
        model = CustomUser
        fields = ('username',
                  'date_company_joined',
                  'phone_number',
                  'career',
                  'rank',
                  'date_company_joined',
                  'gender',
                  'id_number',
                  'department',
                  'resume',
                  'id_photo',
                  'password1',
                  'password2')  # <- User μƒμ„±μ‹œ 보여쀄 field 을 μ •μ˜ν•œλ‹€.

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # null=True둜 μ„€μ •ν•œ ν•„λ“œμ— λŒ€ν•΄ requiredλ₯Ό False둜 μ„€μ •
        self.fields['resume'].required = False
        self.fields['id_photo'].required = False
  • UserCreationForm의 상속 κ΄€κ³„λŠ” μ•„λž˜μ™€ κ°™λ‹€. forms.ModelForm β†’BaseUserCreationForm β†’ UserCreationForm ModelForm 이 λΆ€λͺ¨ ν΄λž˜μŠ€μ΄λ‹€. ModelForm μ‚¬μš© λ°©λ²•λŒ€λ‘œ UserCreateForm 을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.
  • image field 을 μ‚¬μš©ν• λ €λ©΄ Pillow package κ°€ ν•„μš”ν•˜λ‹€. pip install Pillow

2.3 Signup λͺ¨λΈ 등둝

  • settings.py 에 λͺ¨λΈ 등둝
# settings.py
AUTH_USER_MODEL = 'account.CustomUser'

2.4 Migrate

  • python manage.py makemigrations
  • python manage.py migrates

2.5 Signup λͺ¨λΈ

2.5.1 HTML Tempalte

  • 폴더 생성

     mkdir -p account/templates/account
  • templates 생성

    touch account/templates/account/signup.html
    <!-- templates/registration/login.html -->
    <h2>Signup</h2>
    <form action="" method="post" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Signup</button>
    </form>
    
    {{ errors }}

2.5.2 View

  • νšŒμ›κ°€μž…

    def signup(request):
        if request.method == 'POST':
            form = CustomUserCreationForm(request.POST, request.FILES)  # <-- FILES 와 같이 μž…λ ₯이 λ˜μ–΄μ•Ό ν•œλ‹€.
            if form.is_valid():
                # file field 데이터 μ €μž₯
                inst = form.save(commit=False)
    
                # file field 데이터 μ €μž₯
                id_photo = form.cleaned_data['id_photo']
                inst.id_photo.save(id_photo.name, id_photo)
    
                # Signup κ²°κ³Ό νŽ˜μ΄μ§€
                return HttpResponse('Signup complete')
            else:
                print('Errors:', form.errors)
                return render(request, template_name='account/signup.html', context={'errors': form.errors})
    
        elif request.method == 'GET':
            form = CustomUserCreationForm()  # <-바뀐 λΆ€λΆ„
            return render(request, template_name='account/signup.html', context={'form': form})
    
        else:
            raise NotImplementedError

2.5.3 URLConf 등둝

  • account URLConf 등둝

    # ams/account/urls.py
    from django.urls import path, include
    from account.views import signup
    
    app_name = 'account'
    urlpatterns = [
        path('signup/', signup, name='signup')
    ]

βœ… Checkpoint

signup

2.5.4 Media 파일 μ €μž₯ μœ„μΉ˜ μ„€μ •

# settings.py
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
# ams/urls.py
from django.conf.urls.static import static
from ams import settings

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

3. Login & Logout

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

3.1 Login

3.1.1 Login template μœ„μΉ˜ λ³€κ²½

  • LoginView class 둜 이동 ν›„ μ•„λž˜ μ½”λ“œλ‘œ λ³€κ²½ LoginView λŠ” ν•΄λ‹Ή μœ„μΉ˜μ— μ •μ˜λ˜μ–΄ μžˆλ‹€. ( django.contrib.auth.views import LoginView )
# django.contrib.auth.views import LoginView <-- μ—¬κΈ°λ‘œ 이동후 λ³€κ²½
class LoginView(RedirectURLMixin, FormView):
    ...
    template_name = "account/login.html"  # <-- ❗️ μ—¬κΈ° 뢀뢄을 λ³€κ²½ν•œλ‹€.
    ...

3.1.2 Html Template 생성

touch account/templates/account/login.html
<!-- templates/account/login.html -->
<h2>Log In</h2>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Log In</button>
</form>

βœ… Checkpoint (3.1, 3.2)

checkpoint_login

(⚠️ django μ—μ„œλŠ” 기본적으둜 login 성곡 μ‹œ accounts/profile/ 경둜둜 redirect ν•œλ‹€.)

3.1.3 Login redirect μ„€μ •

  • 둜그인 이후 κ°€κ³ μž ν•˜λŠ” url 을 settings.py 에 등둝
  • μ•„λž˜ μ˜ˆμ‹œμ—μ„œλŠ” account/profile 둜 둜그인 이후 redicrect ν•˜λ„λ‘ λ³€κ²½ (4. Profile μ—μ„œ μΆ”κ°€ μ…‹νŒ… μ§„ν–‰ μ˜ˆμ •)
# settings.py
LOGIN_REDIRECT_URL = '/account/profile'

3.2 Logout

3.2.1 Logout URL

1.1 auth app 등둝 단계λ₯Ό μ§„ν–‰ν–ˆλ‹€λ©΄ λ‹¨μˆœνžˆ /accounts/logout url 둜 μ ‘μ†ν•˜λ©΄ λœλ‹€.

3.2.2 둜그 μ•„μ›ƒμ‹œ νŠΉμ • νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•˜κ²Œ ν•˜κΈ°

둜그인 νŽ˜μ΄μ§€μ™€ κ°™κ²Œ settings.py 에 logout 성곡 μ‹œ μ΄λ™ν•˜λŠ” νŽ˜μ΄μ§€λ₯Ό μ •μ˜ν•΄ λ‘”λ‹€.

LOGOUT_REDIRECT_URL = 'home' # new

4. Profile

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

4.1 HTML Templates

  • templatetags 생성
mkdir -p account/templatetags
touch account/templatetags/index.py
# account/templatetags/index.py 
from django import template
register = template.Library()

@register.filter
def index(indexable, i):
    return indexable[i]

@register.filter
def getattribute(indexable, attr):
    return indexable.__getattribute__(attr)
  • template profile.html 생성
touch account/templates/account/profile.html
<!-- account/templates/account/profile.html -->
{% load index %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{% for field in field_names %}
    {% with field_type=types|index:forloop.counter0 attr=user|getattribute:field %}
        {% if field_type == 'ImageField' %}
            {{ field }} : <p><img src={{ attr|getattribute:'url' }}></p>
        {% elif field_type == 'FileField' %}
            {{ field }} : <p><a href={{ attr|getattribute:'url' }}>{{ attr }}</a></p>
        {% else %}
            <p>{{ field }} : {{ attr }} : {{ types | index:forloop.counter0 }}</p>
        {% endif %}
    {% endwith %}
{% endfor %}

</body>
</html>

4.2 Profile Forms

# 경둜 : ams/account/forms.py 
class CustomUserProfileForm(UserCreationForm):
    class Meta:
        model = CustomUser
        # νšŒμ› κ°€μž… νŽ˜μ΄μ§€μ—μ„œ 보여쀄 κΈ°λ³Έ μœ μ € ν•„λ“œ
        base_field = ['username',
                      'first_name',
                      'last_name',
                      'email']

        # νšŒμ› κ°€μž… νŽ˜μ΄μ§€μ—μ„œ 보여쀄 μ»€μŠ€ν…€ μœ μ € ν•„λ“œ
        custom_field = ['phone_number',
                        'career',
                        'rank',
                        'date_company_joined',
                        'id_number',
                        'department',
                        'resume',
                        'id_photo',
                        'region']
        fields = tuple(base_field + custom_field)

4.3 Views

from django.db import models

@csrf_exempt
def profile(request):
    user = CustomUser.objects.get(username=request.user)
    # field 이름을 μΆ”μΆœν•©λ‹ˆλ‹€.
    field_names = CustomUserProfileForm.Meta.field 

    # field 별 type 을 μΆ”μΆœν•©λ‹ˆλ‹€.
    types = []
    for name in field_names:
        field = user._meta.get_field(name)
        # field κ°€ Image type 이면 'ImageType' 으둜 νƒ€μž…μ„ μ €μž₯ν•©λ‹ˆλ‹€.
        # λͺ¨λΈ ν•„λ“œμ˜ λ‚΄λΆ€ νƒ€μž…μ€ μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‚¬μš©λ˜λŠ” νƒ€μž…μž…λ‹ˆλ‹€.
        # get_internal_type()으둜 λ°˜ν™˜λ˜λŠ” 값은 FileField μž…λ‹ˆλ‹€.
        if isinstance(field, models.ImageField):
            types.append('ImageField')
        else:
            types.append(field.get_internal_type())
    # template 을 rendering ν•©λ‹ˆλ‹€.
    context = {'user': user, 'field_names': field_names, 'types':types}
    return render(request, template_name='account/profile.html', context=context)

4.4 URLConf

# ams/account/urls.py
app_name = 'account'
urlpatterns = [
    path('signup/', signup, name='signup'),
    path('profile/', profile, name='profile'),
]

βœ… Checkpoint (4.Profile) profile_checkpoint

5. Superuser

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

5.1 Superuser 생성

  • helper.py 파일 생성

    touch ams/account/helper.py
    
    # touch ams/account/helper.py 에 μ•„λž˜ μ½”λ“œ 생성
    from account.models import CustomUser
    
    def createsuperuser():
        CustomUser.objects.create_superuser(username='admin',
                                            email='[email protected]',
                                            password='q1w2e3r4Q!W@E#R$',
                                            first_name='admin',
                                            last_name='admin',
                                            phone_number='01062766596',
                                            career=2,
                                            rank='manager',
                                            date_company_joined='2022-02-02',
                                            id_number='admin',
                                            department='관리뢀',
                                            region='μ„œμšΈ').save()
        return 'Create Superuser : admin '
    if __name__ == '__main__':
        createsuperuser()
  • helper 파일 μ‹€ν–‰

    python manage.py shell
    from account.helper import createsuperuser
    createsuperuser()
    

βœ… terminal 에 μ•„λž˜ λͺ…λ Ήμ–΄ λ³΄μΌμ‹œ 성곡 'Create Superuser : admin '

6. Admin site

λΉ λ₯Έμ΄λ™ : 0. λ“€μ–΄κ°€κΈ° 전에 | 1. auth URL 등둝 | 2. νšŒμ› κ°€μž… μ‹œμŠ€ν…œ | 3.Login & Logout | 4.Profile | 5.Superuser | 6.Admin Site

  • ams/account/admin.py 에 μ•„λž˜ μ½”λ“œ μΆ”κ°€
  • profile page μ—μ„œ λ³΄μ΄λŠ” μœ μ € 속성을 λͺ¨λ‘ 확인 ν•  수 있음
from django.contrib import admin

# Register your models here.

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.forms import CustomUserProfileForm
from account.models import CustomUser


class CustomUserAdmin(UserAdmin):
    model = CustomUser
    list_display = CustomUserProfileForm.Meta.fields


admin.site.register(CustomUser, CustomUserAdmin)

βœ… Checkpoint

admin site
⚠️ **GitHub.com Fallback** ⚠️