Django Custom User 일지
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
Django Custom User 일지
Django Custom User 일지
이거.. 잊어먹기 쉬울것 같아서
미리 정리하려 한다.
Custom User를 사용하기 위해선
기존 유저모델을 수정 수정한 모델을 Auth 모델로 쓸 것임을 설정파일에 저장.
해야한다.
말로는 간단해보인다..
실제로 하는것도 간단해보인다.
그냥 코드를 복붙해서 하면 되기 때문,
https://dev-yakuza.github.io/ko/django/custom-user-model/
위의 사이트를 참고해서 수정해, 이리저리 테스트해봤다.
# app : account # file : account.models from django.db import models from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser, PermissionsMixin) # from django.contrib.auth.models import User # Choice 테스트용 GRADE_IN_ROLE_CHOICES = [ ('새싹', '새싹'), ('나무', '나무'), ('수풀림', '수풀림'), ] class UserManager(BaseUserManager): use_in_migrations = True def create_user(self, email, date_of_birth, full_name, password=None): if not email: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email), date_of_birth=date_of_birth, full_name=full_name, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, date_of_birth, full_name, password): user = self.create_user( email, password=password, date_of_birth=date_of_birth, full_name=full_name, ) # user.is_superuser = True user.is_admin = True user.save(using=self._db) return user class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField( verbose_name='email', max_length=255, unique=True, ) date_of_birth = models.DateField() is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) # is_superuser = models.BooleanField(default=False) # is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(auto_now_add=True) full_name = models.CharField(max_length=20, null=False) grade_in_role = models.CharField( max_length=10, choices=GRADE_IN_ROLE_CHOICES, default='새싹', ) objects = UserManager() # Username을 email로 명시 USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['date_of_birth','full_name'] def __str__(self): return self.email def has_perm(self, perm, obj=None): return True def has_module_perms(self, app_label): return True @property def is_staff(self): return self.is_admin ## settings 파일 수정 INSTALLED_APPS = [ ### ... 'account', # 추가 ## ... ] # settings 파일 내용 추가 AUTH_USER_MODEL = 'account.User' # account 앱의 User를 모델로 쓸 것을 명시 LOGIN_REDIRECT_URL = '/' # 로그인 시 리디렉트 시켜줄 위치를 명시 LOGOUT_REDIRECT_URL = '/' # 로그아웃 시 리디렉트 시켜줄 위치를 명시
자 이것만 해도
실제 데이터베이스 내에 account.models.User 라는 것을 Auth 모델로 쓰게 되고,
데이터도 저런 형식으로 받게 된다.
적용 방법은
python manage.py makemigrations
python manage.py migrate
# on Error >> python manage.py migrate --run-syncdb 후 다시 makemigrations, migrat
하면 된다.
createsuperuser시 저런식으로 정보도 입력받는다. 착한것 ㅎㅎ
자, 근데 여기서 문제가 있다.
기껏 테이블을 만들었는데, 쓰질 않아서 내용이 없다.
아니 쓰는 틀 조차도 없다.
카페에 갔는데, 우산 놓아두라고 놔둔 쓰레기통에다
아무 표기도 없고 아무도 우산을 안 꽂아서, 지나가는 사람들이 휴지나 버리고 있는 격이다.
그러니, 지금 필요한 것은 사용하라는 틀과, 관리자용 페이지에 해당 내용을 추가하는 것이다.
관리자 페이지에 추가를 안하면,,
만약 따로 view단에서 세이브를 시켜준다 한들,,
관리자는 아무 권한도 없는거다.(볼 수 있어야 고치지..)
틀 만들기.(forms.py 작성)
## account.forms # ++ forms.py from django import forms from django.contrib.auth.forms import ReadOnlyPasswordHashField from .models import User # 사용자 생성 폼 class UserCreationForm(forms.ModelForm): password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField( label='Password confirmation', widget=forms.PasswordInput) class Meta: model = User fields = ('email', 'date_of_birth', 'full_name') def clean_password2(self): password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user # 사용자의 자기 정보 변경 폼 class UserChangeForm(forms.ModelForm): password = ReadOnlyPasswordHashField() class Meta: model = User fields = ('email', 'password', 'date_of_birth', 'full_name', 'is_active', 'is_admin') def clean_password(self): return self.initial["password"] # 로그인 폼 class LoginForm(forms.ModelForm): password = forms.CharField(label='Password', widget=forms.PasswordInput) class Meta: model = User fields = ('email', 'password') # 로그인 시에는 유저이름과 비밀번호만 입력 받는다.
여기서 UserCreation은 admin 내에서 사용자를 생성할 때 쓰일 폼이고,
UserChangeForm은 admin 페이지 내에서 사용자 변경을 하거나,
사용자가 자기 정보를 변경할 때 사용하는 용도로 작성해줌
표로 정리하면,
클래스 이름 용도 UserCreation 사용자 생성(Admin, 가입 시) UserChangeForm 사용자 정보 변경(Admin, 사용자 직접 변경) Login 사용자 로그인 폼
그럼, form을 만들었으니,
admin 페이지에 적용시켜 보자.
# account.admin from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from .forms import UserChangeForm, UserCreationForm # 작성한 모델을 사용하는것에 주의 from .models import User class UserAdmin(BaseUserAdmin): form = UserChangeForm add_form = UserCreationForm list_display = ('email', 'full_name', 'date_of_birth', 'is_admin') list_filter = ('is_admin',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('date_of_birth','full_name',)}), ('Permissions', {'fields': ('is_admin','grade_in_role',)}), ) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'date_of_birth', 'password1', 'password2')} ), ) search_fields = ('email','full_name') ordering = ('email',) filter_horizontal = () # User, UserAdmin 을 등록 admin.site.register(User, UserAdmin) # Group 을 등록해제 admin.site.unregister(Group)
그러면
딱 원하는대로 잘 붙어줘 있다. 지금건 UserChangeForm임
User추가를 눌러주면 사용자 추가(UserCreationForm)도 잘 붙어줘 있다.
그러면.. Custom User의 진정한 목표
사용자의 프로필을 붙여보자.
나의 경우 여기서 시행착오가 상당히 많았다.
버그인지는 모르겠다.
왜냐하면
뭔 짓을 해도,
모델 부분 에 아무리 뭔가를 추가해도..
기존 작성했던 model 부분에 뭔가를 아무리 열심히 추가로 작성해도
도저히 makemigration, migrate, migrate --run-syncdb
가 먹히지 않는다.
account의 migrations 파일들도 생성되지 않는다.
하지만 여기서 굳이 생성을 하고자 한다면,
db.sqlite3를 삭제 해버리는 수가 있긴 했다.
근데 모델이 어멍 자주 바뀌는데 매번 삭제할 수가 없는 노릇,,
이때를 위해서 사용하는 방법이 있었다.
https://cjh5414.github.io/extending-user-model-using-one-to-one-link/
그 방법은 User 모델을 onetoone 모델로 불러와서, 이에 추가하고 싶은 정보를 저장하는 방법이다.
# profile.py < 추가 작성 from django.db import models # from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver from .models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) location = models.CharField(max_length=30, blank=True) in_company_name = models.CharField(max_length=30, blank=True) # 세이브 전 실행되라는 의미의 데코레이터 @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) # 세이브 전 실행되라는 의미의 데코레이터 @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): instance.profile.save()
그러면, 매 번 User를 호출해서 관련 정보를 저장할 때마다, Profile도 불러오고, id도 불러오고
저장해야 하느냐.. 그건 아니다.
그걸 해주는 것이 바로 데코레이터다.
User가 저장되기 전, 위와 같은 정보를 포함하여 저장하게 될 것이다.
내용이 추가됐으니, form도 추가돼야 할 것이다.
써야할 내용이 늘어났으니까!
수정하여 사용하거나, 추가해서 사용하는건 마음대로이나, 일단 코드부터 이해해보는게 중요한 듯 하다.
뷰로 한 번 봐보자.
### forms.py ## .... # .... # 위에서 만든 UserCreationForm을 상속받고, # 필요한 내용을 추가한 form을 생성한다. class ProfileForm_withCreation(UserCreationForm): location = forms.CharField(max_length=30) in_company_name = forms.CharField(max_length=30) class Meta: model = User fields = ('email', 'date_of_birth', 'full_name', 'location', 'in_company_name','password1','password2') # views.py from django.shortcuts import render, redirect, HttpResponse from .forms import UserCreationForm, LoginForm, ProfileForm, ProfileForm_withCreation from django.contrib.auth import authenticate, login, get_user_model from account.models import User from django.contrib import messages # ... # ... def signup_combined(request): if request.method == "POST": form = ProfileForm_withCreation(request.POST) print(form.is_valid()) print(form.errors) if form.is_valid(): user = form.save(commit=False) user.save() return redirect('home') else: form = ProfileForm_withCreation() data = { 'form':form, } return render(request, 'account/signup.html', data)
회원가입
만약 이 위에서 만든,
ProfileForm_withCreation 을 admin 에서 쓰게 되면
똑같은 내용을 출력시킬 수 있다.
이렇게 구성하면서 생긴 이득,
migration 걱정을 안해도 됨(내 지식선상에서 기존 db를 날려야만 작업가능 했었음..)
추가적으로 필요한 부분 계속하여 추가 삭제 가능.
form을 작성해두었으니 재사용이 용이.
했더랬다.
정리끝.
가입폼 완성 예시
Profile form과
UserCreationForm 을 따로
뷰 단에서 불러들여 쓰게 된다면,
저장이야 잘 되겠지만, Form.is_vaild()가 안될 수도 있다.
(Form.is_valid()가 True 여야만 Form.cleaned_data 를 사용할 수 있다.)
내 경우 안됐었기 때문에, 다른 방법삼아 그냥 가입폼을 따로 만들어버렸다.
from http://nadure.tistory.com/13 by ccl(A)
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기