컴굥일지

[Django Study 10] Profileapp Implementation 본문

프로그래밍 강의/Django

[Django Study 10] Profileapp Implementation

gyong 2022. 4. 25. 04:22
반응형

[Profileapp 시작 그리고 ModelForm]

Profile

  • Account 객체와 1:1로 매칭된다.
  • delete, detail 기능은 만들지 않는다. Account 객체가 탈퇴하면 자동으로 사라지게 된다.

Profile app 생성

  1. 터미널에 python manage.py startapp profileapp을 입력하여 앱 생성

  2. settings.py파일에 가서 INSTALLED_APPS에 가서 ‘profileapp’ 추가

  3. pragmatic/urls.py에 가서 아래 코드를 작성하여 프로필 페이지로 연결해주는 경로를 생성

     path('profiles/', include('profileapp.urls')),
  4. profileapp파일로 가서 urls.py 만들고, app_name 설정한다.

  5. 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)
  6. 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 클래스 안에 코드를 작성하면 된다.
  7. model을 만들었으니 python manage.py makemigrationspython manage.py migrate를 터미널에 입력한다.



[Profileapp 구현 시작]

  1. 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을 보낸 당사자를 입력한 뒤에 저장하도록 한다.
  2. 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')
     ]
  3. 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 마무리]

  1. 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"
  2. 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> 필요
     ]
  3. 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 %}
  4. 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>
  5. 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)
  6. 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강)

https://www.inflearn.com/course/%EC%9E%A5%EA%B3%A0-%ED%95%80%ED%84%B0%EB%A0%88%EC%8A%A4%ED%8A%B8/dashboard

반응형
Comments