자 여기를 보면 아니 request는 처음 요청하면 생기고 요청된 데이터를 전부 호함하고 있는거 아닌가? 왜 저렇게 .user ,.POST 각각 따로 가지고 있지? 자, 여기서 조금 오해가 생길 수 있습니다. 자, request.POST는 HTML 폼에서 보낸 데이터를 딕셔너리처럼 저장한 것입니다. <form method="POST">로 제출된 모든 데이터가 여기에 들어갑니다. 예를 들어 폼에 "이름", "이메일" 필드가 있다면, 그 값들이 request.POST에 담깁니다.
request.user는 현재 로그인한 사용자의 정보가 들어있는 User 객체이며, 이 객체는 Django가 알아서 request에 붙여줍니다. 즉, request.user는 로그인한 사용자의 기존 데이터를 가지고 있습니다.
여기서 핵심은 기존 데이터와 새로 제출된 데이터를 동시에 다루기 위해서입니다. request.user → 기존 사용자 정보 (수정 이전 상태) request.POST → 사용자가 폼에서 입력한 새 데이터 (수정할 값)
request.POST: 사용자가 폼에 입력한 데이터 (새 데이터)를 가져옵니다. instance=request.user: 어떤 객체를 수정할지 Django에게 알려줍니다. (기존 데이터).
Django는 이 두 데이터를 비교해서: request.user를 기반으로 폼을 채우고, request.POST의 값으로 기존 데이터를 업데이트합니다.
한 마디로, 저 코드에서 request.POST는 만약 사용자가 DB에 정보를 바꾼다면 그 값을 들고오는 것이며, 그와 동시에, request.user는 이미 DB에 존재하기 때문에(중요),기존 정보를 화면에 보여주기 위함으로 넣는것입니다. 즉, request.user는 instance로 인해 화면에 시각화가 되며(이미 DB에 있기 때문에), 만약 사용자가 정보를 바꾼다면, 그 바뀐 정보(왜냐하면 POST이기 때문)를 가지고 와서, save를 한다(즉, 바꾼다)라는 뜻입니다. 그리고 만약에 그냥 정보를 바꾸기 위해 변경 링크로 들어왔다면, 그냥 화면에 기존 정보를 보여줍니다.
그래서 if ...POST와 else GET이 있다는 뜻입니다. GET은 사용자가 만약 로그인이 되어있고, 정보가 DB에 있으며, 그냥 기존 정보를 바꾸러 왔다면 그것은 DB에 정보의 변화, 혹은 변경된 사항이 아직 없기 떄문에 GET으로 들어가 기존 정보를 보여주는 것이고,
POST는 사용자가 변경하기를 누른다면 그때는 DB의 변화가 있기 때문에 POST로 request가 들어와 if..POST가 실행되어, 바뀐 정보(request.POST)를 save한다는 뜻입니다.
어? 그러면 POST에서는 그냥 바뀐 정보만 들고오면 되지 않나? 왜 다시 instance = request.user를 쓰는거지 하실수 있습니다. 맞습니다, 겉으로 보면 바뀐 정보만 request.POST에서 가져와 저장하면 되지 않나? 하고 생각할 수 있지만, instance=request.user는 기존 데이터와 새로운 데이터를 "연결" 해주기 때문에 매우 중요합니다.
instance=request.user를 사용하면 Django가 기존 객체를 수정하는 것이라는 걸 이해하고, 기존 데이터를 덮어쓰면서 업데이트를 해줍니다. 만약 instance를 쓰지 않으면 새로운 객체를 생성해버릴 수 있음과 동시에, 저희는 기존 사용자의 데이터를 업데이트해야 하니까, instance=request.user가 필요합니다.
즉, else에서는 기존 정보를 그대로 가져와서 context를 통해 그대로 보여지지만, POST에서는 그 정보를 들고와서 바꿔주는 그릇의 용도로 쓰인다는 뜻입니다. 그렇기에 rrequest.POST는 바뀐 정보를 들고 있지만, instance는 기존 정보를 그대로 들고 있음으로써, 바뀌는 기존 user의 정보를 알려주는 역할을 한다는 뜻입니다. 예: instance = request.user -> 너가 바꾸고 싶은 정보있지? 그 정보는 내가 들고 있는 정보야! request.POST -> 아? 그거야? 알았어 그거 들고 있어 내가 곧 새로운 정보를 줄게! 그걸로 바꿔! 이런 느낌입니다.
비밀번호 변경????!!!!!
장고에서 비밀번호를 바꿀려면 changepasswordform이 있다.
자, 여기서 주요한 정보가 있습니다. PasswordChangeForm를 ctrl누르고 눌러보면, 그 클래스가 가져야한 파라미터를 확인할 수 있습니다.
(사실 여긴 아직 안 나와서 짧게 찍은...) 아직 여기서 보기에는 특별한 정보가 없습니다. 하지만 위에 SetPasswordForm을 상속받는 것을 볼 수 있습니다. 그래서 저길 들어가보면
저기 __init__(init을 봐야함)을 보면 user가 있는 것을 볼 수 있습니다. 이렇게 아, request.user는 필수 파라미터구나 라고 알 수 있습니다.
그런데 passwordform은 instance 파라미터를 사용하지 못합니다. 왜냐? 여기선 Model 폼과 일반 폼의 개념이 있습니다.
모델 폼? 일반 폼?
ModelForm과 일반 Form의 차이를 이해하는 것은 Django에서 폼을 사용하는 데 중요한 개념입니다. 아래에 두 폼의 주요 차이점을 정리해 드릴게요.
1. ModelForm (모델 폼) ModelForm은 Django 모델과 연결된 폼입니다. 모델의 데이터를 직접적으로 다루기 위한 폼입니다.
자동으로 모델 필드를 폼 필드로 변환합니다. ModelForm은 모델 클래스와 연결되므로, 모델의 필드가 자동으로 폼의 입력 필드로 변환됩니다. 예를 들어, User 모델의 username 필드가 ModelForm에서 폼 필드로 변환됩니다.
class MyModel(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['name', 'age']
# 모델 인스턴스에서 폼을 생성
my_model_instance = MyModel.objects.get(id=1)
form = MyModelForm(instance=my_model_instance)
모델 인스턴스를 통해 데이터를 처리합니다. instance 파라미터를 사용하여 특정 모델 인스턴스를 불러와 그 데이터를 폼에 채울 수 있습니다. 이 데이터를 수정한 후 저장하면 모델 인스턴스가 자동으로 업데이트됩니다.
자동으로 데이터 저장: ModelForm은 save() 메서드를 통해 모델 인스턴스에 데이터를 자동으로 저장합니다.
2. 일반 Form (일반 폼) 일반 Form은 Django의 기본 폼으로, 데이터 처리와는 별개로 폼의 구성만 담당합니다. 모델과 연결되지 않으므로 모델의 데이터베이스와는 직접적인 관계가 없습니다.
특징 모델과 연결되지 않음: 일반 Form은 모델과 연결되지 않고, 수동으로 필드를 정의합니다.
class MyForm(forms.Form):
name = forms.CharField(max_length=100)
age = forms.IntegerField()
데이터베이스와의 연동이 필요 없을 때 사용: 데이터베이스에 저장할 필요가 없거나, 폼 데이터가 모델과 관련이 없는 경우 사용합니다. 저장 작업은 수동: Form은 save() 메서드가 없으며, 데이터를 저장하거나 처리하는 작업을 수동으로 처리해야 합니다.
form = MyForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
age = form.cleaned_data['age']
# 데이터를 DB에 저장하는 로직을 직접 작성
차이점
그런데, 제일 중요한점! 어떤게 모델 폼이고 어떤게 일반 폼인지 어떻게 알 수 있는가? 그건 아까 말했던 class를 계속해서 보면 됩니다.
UserChangeForm을 봤을 때 상속 받는건 뭐다? 바로 ModelForm입니다.
하지만 그와 반대로 PasswordChangeForm은
다른 것을 상속을 받는데, 저 클래스를 확인해도
forms.form, 즉 일반 폼을 상속 받습니다.
그렇기 때문에 일반 폼은 instance를 쓸 수 없습니다 왜냐하면 instance는 ModelForm에서 사용되기 때문입니다. instance=request.user를 전달하면 해당 모델 인스턴스의 데이터를 폼에 채워 넣고 수정할 수 있기 때문입니다.
그러면 도대체 저 instance라는 녀석은 뭔데??
instance는 모델 인스턴스를 의미합니다. ModelForm에서 instance 파라미터는 폼을 통해 특정 모델 인스턴스를 불러오고 수정할 수 있게 도와주는 역할을 합니다.
instance가 하는 일 (1) 데이터 로딩 (폼에 데이터 채우기) ModelForm을 사용할 때, instance에 모델 인스턴스를 전달하면, 해당 인스턴스의 데이터를 폼에 채워 넣습니다. 즉, 기존 데이터를 폼 필드에 표시할 수 있게 되는 것입니다.
예를 들어, User 모델이 있고 UserChangeForm이라는 ModelForm을 사용한다고 가정할 때,
# 모델 인스턴스를 가져옵니다.
user_instance = User.objects.get(id=1)
# 폼을 생성할 때 인스턴스를 전달합니다.
form = UserChangeForm(instance=user_instance)
여기서 instance=user_instance는 user_instance 객체의 데이터를 폼 필드에 자동으로 채워넣습니다.
예시: User 모델의 username, email 등의 필드가 폼에 자동으로 표시되고, 사용자는 그 값을 수정할 수 있습니다.
(2) 데이터 저장 (모델 인스턴스에 수정 사항 반영) 폼에서 수정된 데이터를 저장할 때, save() 메서드를 호출하면, 기존의 모델 인스턴스를 업데이트하거나 새로운 모델 인스턴스를 생성할 수 있습니다.
기존 인스턴스를 수정하는 경우: instance로 전달된 모델 객체에 대해 save()를 호출하면, 그 객체의 데이터를 수정하고 데이터베이스에 반영합니다.
if form.is_valid():
form.save() # 전달된 instance의 데이터를 업데이트
새로운 인스턴스를 생성하는 경우: instance가 제공되지 않으면, save() 메서드는 새로운 객체를 생성하여 저장합니다.
instance를 사용하지 않았을 때와 사용했을 때 차이 (1) instance를 사용하지 않은 경우 instance를 전달하지 않으면, 폼은 새로운 데이터를 처리하는 데만 사용됩니다. 즉, 기존 데이터를 가져와 수정하는 것이 아니라, 빈 폼으로 처리되고, 사용자가 입력한 데이터를 새로 저장합니다. 즉, instance를 사용하지 않으면, 기존 데이터를 폼에 넣는 데 수동으로 작업이 필요합니다.
# instance를 전달하지 않은 경우
form = UserChangeForm(request.POST)
if form.is_valid():
form.save() # 새 모델 인스턴스를 생성하여 저장
(2) instance를 사용한 경우 instance를 전달하면, 폼은 기존 데이터를 수정하는 방식으로 작동합니다. 즉, 기존 모델 인스턴스를 가져와서 수정하고, 이를 데이터베이스에 반영합니다.
# instance를 전달한 경우
form = UserChangeForm(request.POST, instance=request.user) # 기존 데이터로 폼 채우기
if form.is_valid():
form.save() # 기존 모델 인스턴스를 수정하여 저장
여기서 instance=request.user는 현재 로그인한 사용자(request.user)의 데이터를 폼에 채워 넣고, 사용자가 폼에서 수정한 데이터를 그 사용자 객체에 저장합니다.
그런데 이상하지 않나요? 분명 PasswordChangeForm은 일반 폼인데 .save()를 쓰고 있습니다. 어? 일반 폼은 사용못 한다고 하지 않았어?라고 하실 수 있습니다. 여기서도 트릭이 있습니다... 쓸 수 있는 이유는, PasswordChangeForm이 사실상 ModelForm을 상속받지 않더라도 User 모델과 연관된 폼이기 때문입니다. PasswordChangeForm은 Django의 기본 제공 폼 중 하나로, 사용자의 비밀번호를 변경하는 용도로 사용됩니다. 이 폼은 User 모델을 직접 수정하는 기능을 포함하고 있기 때문에, .save() 메서드를 통해 사용자의 비밀번호를 안전하게 변경할 수 있습니다. 즉, 내부적으로는 User 모델에 대한 변경 작업을 처리하고, User 모델의 set_password 메서드를 사용하여 비밀번호를 해시 처리합니다.
아주 좋게 만들어졌습니다. 단, 여러분.....에러를 발견했습니다. 기억하세요 form2 = PasswordChangeForm(request.POST, request.user)에서 request.user가 첫번째 인자로 들어가야 한다는 것을...
갑자기 세션?
자, 여기서 만약 비밀번호를 바꾸게 된다면, 서버가 가지고 있는 세션 테이블에서 세션 ID가 아니라 ID로 접근하는 User의 정보의 value에서 검증했던 세션의 value랑 바뀐 비밀번호랑 일치하지 않게 됩니다. 그렇기 때문에 장고가 다 지운것입니다.(재연결 필요) 즉, 바뀐 비밀번호를 가지고 있는 유저의 세션을 다시 연결하는 작업이 필요합니다. 그것이 위에 적었던 update_session_auth_hash(request, form2.user)을 쓰는 겁니다. 참고로 첫번째 인자로 request를 넣고, 두번째로 form에 들어있는 user꺼내서 주면 됩니다.(그냥 새로고침? 한다는 느낌!)
그런데 일단 저는 passwordform과 같이 적긴 했지만, 사실은
이렇게 해놨기에 안 보일뿐, 실제로 비밀번호 바꾸는 링크도 따로 만들어야 합니다.
실은 이렇게 나와야 합니다. 실제로 이것은 따로 확인해야 할 사항이며, 따로 확인을 하면,
이렇게 경로가 있습니다. 이것은 아마 forms.py에 있을 확률이 높으며,
이렇게 먼저, UserChangeForm으로 가면
이곳에 패스워드가 있는 것을 확인할 수 있습니다. 하지만 강의와 장고 버전이 달라 이부분은 그냥 스킵..
원래 이래야합니다. 그리고 저 init부분을 전부 forms로 가져와서 오버라잇 해야합니다.
일단 여기서 나오는 중요한 키워드 하나 알고 가겠습니다 reverse()입니다.
reverse는 Django에서 URL을 역으로 찾는 함수입니다. 주로 URL 패턴을 직접적으로 작성하는 대신, 뷰 함수의 이름이나 URL 이름을 사용해 URL을 동적으로 생성할 때 사용합니다.
1. URL 패턴에서 이름을 지정한 경우
# urls.py에서 URL에 이름을 지정
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login_view, name='login'), # 'login'이라는 이름 지정
]
2. reverse를 사용하여 URL을 생성
from django.urls import reverse
# 'login' URL 이름을 사용해서 URL을 역으로 찾기
url = reverse('login') # '/login/'이 반환됨
reverse의 주요 인자 viewname: URL 이름 (예: login). args: URL 경로에서 변수가 필요하다면, 해당 변수를 튜플이나 리스트 형태로 전달합니다. kwargs: URL 경로에서 변수가 필요할 때, 이름-값 쌍을 사용하여 넘길 수 있습니다.