컴굥일지
[Django Study 10] Profileapp Implementation 본문
[Profileapp 시작 그리고 ModelForm]
Profile
- Account 객체와 1:1로 매칭된다.
- delete, detail 기능은 만들지 않는다. Account 객체가 탈퇴하면 자동으로 사라지게 된다.
Profile app 생성
터미널에 python manage.py startapp profileapp을 입력하여 앱 생성
settings.py파일에 가서 INSTALLED_APPS에 가서 ‘profileapp’ 추가
pragmatic/urls.py에 가서 아래 코드를 작성하여 프로필 페이지로 연결해주는 경로를 생성
path('profiles/', include('profileapp.urls')),
profileapp파일로 가서 urls.py 만들고, app_name 설정한다.
models.py에서 아래 코드를 작성한다.
class Profile(models.Mode): #프로파일의 주인이 누구인지 정해준다 #OneToOneField Profile과 user객체를 하나씩 연결해준다 #on_deleted : 연결되어있는 User 객체가 사라질 때 #Cascade : 그와 연결되어있는 profile도 같이 사라진다. #related_name : user 객체에 접근할 때, 따로 profile객체를 찾지 않고 바로 연결해준다. #ex) request.user.profile처럼 바로 프로필을 찾을 수 있다. user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') #upload_to : 이미지를 받아서 서버 내부에 저장하는데 어느 경로에 저장될지 #null=True : 이미지 꼭 안 올려도 된다 image = models.ImageField(upload_to='profile/', null=True) # media/profile~ #unique=True : 닉네임은 서로 겹치면 X nickname = models.CharField(max_length=20, unique=True, null=True) message = models.CharField(max_length=100, null=True)
forms.py를 작성한다.
from django.forms import ModelForm from profileapp.models import Profile class ProfileCreationForm(ModelForm): class Meta: model = Profile fields = ['image', 'nickname', 'message']
- accountapp과는 달리, profileapp은 ModelForm을 상속받는다.
- 상속받은 이후, Meta 클래스 안에 코드를 작성하면 된다.
model을 만들었으니 python manage.py makemigrations와 python manage.py migrate를 터미널에 입력한다.
[Profileapp 구현 시작]
views.py를 만든다.
#profileapp/views.py class ProfileCreateView(CreateView): model = Profile context_object_name = 'target_profile' form_class = ProfileCreationForm success_url = reverse_lazy("accountapp:hello_world") template_name = "profileapp/create.html" def form_valid(self, form): #바로 저장하지 않고 임시로 저장 commit=False temp_profile = form.save(commit=False) temp_profile.user = self.request.user temp_profile.save() return super().form_valid(form)
- NOT NULL constraint failed: profileapp_profile.user_id 라는 오류가 발생하는 것을 막기 위해, form_valid() 메서드를 구현한다.
- form을 바로 저장하는 것이 아니라, profile의 user 데이터에 form을 보낸 당사자를 입력한 뒤에 저장하도록 한다.
create.html을 만들고, urls.py에 url도 입력한다.
<!-- create.html --> {% extends 'base.html' %} {% load bootstrap4 %} {% block content %} <div style = "text-align:center; max-width: 500px; margin:4rem auto"> <div class = "mb-4"> <h4>Profile Create</h4> </div> <!-- 이미지 파일을 입력받고자 할 때는, enctype을 꼭 넣어줘야 한다. 없으면 파일 못 받음--> <form action = "{% url 'profileapp:create' %}" method = "post" enctype="multipart/form-data"> {% csrf_token %} {% bootstrap_form form %} <input type = "submit" class = "btn btn-dark rounded-pill col-6 mt-3"> </form> </div> {% endblock %}
urlpatterns =[ path('create/', ProfileCreateView.as_view(), name='creat') ]
accountapp/detail.html을 수정한다.
<!-- 프로필이 있으면 그걸 보여주고, 없으면 생성하는 링크를 알려준다--> {% if target_user.profile %} <h2> {{ target_user.profile.nickname }} </h2> {% else %} <a = href="{% url 'profileapp : create' %}> <h2> Create Profile </h2> </a> {% endif %}
[Profileapp 마무리]
UpdateView 생성
#profileapp/views.py class ProfileUpdateView(UpdateView): model = Profile context_object_name = 'target_profile' form_class = ProfileCreationForm success_url = reverse_lazy("accountapp:hello_world") template_name = "profileapp/update.html"
urls.py 에 추가
#profileapp/urls.py urlpatterns = [ path("create/",ProfileCreateView.as_view(),name="create"), path("update/<int:pk>",ProfileUpdateView.as_view(),name="update"), #update는 <int:pk> 필요 ]
html 만들기 - update할 때에도 이미지 파일이 있으니까 enctype 넣어주기
{% extends 'base.html' %} {% load bootstrap4 %} {% block content %} <div style = "text-align:center; max-width: 500px; margin:4rem auto"> <div class = "mb-4"> <h4>Update Profile</h4> </div> <form action = "{% url 'profileapp:update' pk=target_user.pk %}" method = "post" enctype="multipart/form-data"> {% csrf_token %} {% bootstrap_form form %} <input type = "submit" class = "btn btn-dark rounded-pill col-6 mt-3"> </form> </div> {% endblock %}
detail 페이지 update 기능에 맞게 수정
닉네임을 바꿀 수 있는 링크 생성
{% if target_user.profile %} <!-- 바로 아래 이미지는 5번 설정까지 완료해야 볼 수 있다 --> <img src="{{ target_user.profile.image.url }} " alt="" style="height: 12rem; width:12rem; border-radius:20rem; margin-bottom:2rem;"> <h2 style="font-family: 'NanumSquareB';"> {{ target_user.profile.nickname }} <a href="{% url 'profileapp:update' pk=target_user.profile.pk %}"> edit </a> </h2> {% else %} <a href="{% url 'profileapp:create' %}"> <h2 style="font-family: 'NanumSquareB';"> Create Profile </h2> </a> {% endif %} <h5 style="margin-bottom:3rem;"> {{ target_user.profile.message }} </h5>
detail 페이지 이미지 출력
#pragmatic/urls.py from django.conf.urls.static import static #이거 새로 추가할 것 urlpatterns = [ path('admin/',admin.site.urls), path('accounts/',include('accountapp.urls')), path('profiles/',include('profileapp.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
decorators.py 만들고 views.py에 추가
#profileapp/decorators.py from django.http import HttpResponseForbidden from profileapp.models import Profile def profile_ownership_required(func): def decorated(request, *args, **kwargs): user = Profile.objects.get(pk=kwrgs['pk']) if not profile.user == request.user: return HttpResponseForbidden() return func(request, *args, **kwargs) return decorated
#profileapp/views.py #ProfileUpdateView 위에 추가 @method_decorator(profile_ownership_required, 'get') @method_decorator(profile_ownership_required, 'post')
[get_success_url 함수 그리고 리팩토링]
update이후 hello_world 페이지가 아니라 detail 페이지로 보내고 싶음
success_url = reverse_lazy(’accountapp:detail’) 로 바꾼다고 되지 않는다. (pk가 없기 때문)
따라서 내부 메서드를 수정해야 한다. (이때는 reverse를 써도 된다)
class ProfileUpdateView(UpdateView): model = Profile context_object_name = 'target_profile' form_class = ProfileCreationForm #success_url = reverse_lazy('accountapp:detail') #이 방식으로는 detail페이지로 못 감 #pk를 전해줄 수 없어서. 그래서 내부 메소드를 수정해주어야 한다. template_name = "profileapp/update.html" #내부 메서드를 수정해야 한다. def get_success_url(self): return reverse("accountapp:detail",kwargs={'pk':self.object.user.pk}) # self.object가 위에 있는 Profile이다. 따라서 연결되어있는 user의 pk를 얻을 수 있다.
profile의 detail.html을 약간 수정해주자
{% extends 'base.html'%} {% block content %} <div style="text-align: center; max-width: 500px; margin: 4rem auto"> <div> {% if target_user.profile %} <img src="{{target_user.profile.image.url}}" alt="" style="height: 12rem; width:12rem; border-radius:20rem; margin-bottom:2rem;"> <h2 style="font-family:NanumSquareB"> {{ target_user.profile.nickname }} {% if target_user==user %} <a href="{% url 'profileapp:update' pk=target_user.profile.pk %}"> edit </a> {% endif %} </h2> <h5 style="margin-bottom:3rem;"> {{ target_user.profile.message }} </h5> {% else %} {% if target_user==user %} <a href="{% url 'profileapp:create' %}"> <h2 style="font-family: 'NanumSquareB'"> Create Profile </h2> </a> {% else %} <h2> 닉네임 미설정 </h2> {% endif %} {% endif %} {% if target_user == user %} <a href="{% url 'accountapp:update' pk=user.pk %}"> <p> Change Info </p> </a> <a href="{% url 'accountapp:delete' pk=user.pk %}"> <p> Quit </p> </a> {% endif %} </div> </div> {% endblock %}
[출처] 작정하고 장고! Django Pinterest 따라만들기 : 바닥부터 배포까지
섹션 4. Profileapp Implementation (30강 ~ 33강)
'프로그래밍 강의 > Django' 카테고리의 다른 글
[Django Study 12] Commentapp Implementation (0) | 2022.04.27 |
---|---|
[Django Study 11] Articleapp Implementation (0) | 2022.04.26 |
[Django Study 09] Authentication 인증시스템 구현과 Decorator (0) | 2022.04.24 |
[Django Study 08] DetailView, UpdateView, DeleteView 를 통한 accoutapp 구현 (0) | 2022.04.20 |
[Django Study 07] Login / Logout 구현 및 Bootstrap을 통한 디자인 (0) | 2022.04.19 |