30강)

Profileapp 시작 그리고 ModelForm

 

일단 그전에 문제점 하나

 

로그인 하고 다른 detail 페이지 들어가면, 다른 사람 아이디가 그대로 보여

번호만 바꿔주면

나는 test1 아이디로 로그인했는데, 파라미터 값만 바꿔주니까 admin의 아이디가 보이는거야

아 지금 얘기 들어보니까, 그게 문제가 아니네

그냥 아이디가 출력되는게 문제지.

지금 구상을 인스타그램처럼 생각하고 있는거같음. 노출되는 것 자체는 괜찮은듯.

 

이제 이 아이디를 닉네임으로 바꿔줄거야.

 

이제 Profileapp을 만들어줄건데...

Account 앱과 Profile은 1:1 매칭이 될거야.

 

만들 것 :

Profile Image

Profile Nickname

Profile Message

 

Profile Delete View나 Profile Detail View는 안만듦

일단 Profile View랑 accountapp이랑 1:1 연동이니까 account 삭제되면 같이 삭제되게 하면 되니까 delete view 노쓸모

그리고 Profile만 따로 볼 수 있는 Detail 페이지를 구현하지는 않을거야

왜? 

메인 페이지에서 Profile 그냥 뽑아내줄거라서 굳이 Profile 페이지 자체를 만들지는 않을거야

 

python manage.py startapp profileapp

 

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accountapp',
    'bootstrap4',
    'profileapp'
]

 

일단 profileapp에서 urls.py 만들고

app_name = 'profileapp'

urlpatterns = [

]

 

그 다음 models.py 가서

 

class Profile(models.model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')

OneToOneField은 Profile의 User와 Accountapp의 User를 1:1 맵핑시켜주는거. 장고가 제공.

on_delete는 지워졌을 때 뭐 해라

USER 객체가 사라졌을 때, CASCADE = 이 페이지도 없애라.

CASCADE말고 다른것도 있는데 그건 장고 documentation 확인해라.

 

즉 해석하면, 일단 Profile의 User와 Accountapp의 User를 1:1 연결시킨다

그리고 CASCADE 이거는, Accountapp의 User 삭제됐을 때 Profile의 User도 삭제한다.

 

related_name='profile'은?

 

굳이 어렵게 profile을 참조하지 않고, request.user.profile로 접근할 수 있도록 해줌

 

예를 들어서, nickname = models.CharField() 라는게 있다고 하면,

request.user.profile.nickname으로 바로 받아올 수 있음.

 

 

media 폴더 밑에 profile 밑에 이미지들이 다 저장되는거야.

이 설정은 나중에 해준대

 

CharField에서 unique=True 이거는 이 닉네임은 하나이고 중복없어야된다는 뜻

 

 

자 이제 Form을 만들어보자

Account 같은 경우 Django가 Form을 지원해줘.

근데 Profile은 지원 안해줘

그럼 우리가 만들어야돼

 

그러면 너무 불편하니까 Model Form이라는게 있어

기존의 Model을 Form으로 변환해주는거야

 

profileapp에서 forms.py 만들어

from django.forms import ModelForm
from profileapp.models import Profile

class ProfileCreationForm(ModelForm):
    class Meta:
        model = Profile
        fields = ['image', 'nickname', 'message']

Model 만들었자나? 그러면 디비에 반영해주는 작업이 필요해

 

from django.contrib.auth.models import User
from django.db import models

# Create your models here.
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')

    image = models.ImageField(upload_to='profile/', null=True)
    nickname = models.CharField(max_length=20, unique=True, null=True)
    message = models.CharField(max_length=100, null=True)

 

그 다음에 python manage.py makemigrations

python manage.py migrate

 

일케 하면 돼

 

profileapp/views.py

from django.shortcuts import render

# Create your views here.
from django.urls import reverse_lazy
from django.views.generic import CreateView

from profileapp.forms import ProfileCreationForm
from profileapp.models import Profile


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'

profileapp/templates/profileapp/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>

    <form action="{% url 'profileapp:create' %}" method="post">
        {% csrf_token %}
<!--        {{ form }}-->
        {% bootstrap_form form %}
        <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
    </form>
</div>

{% endblock %}

 

글구 profileapp의 urls.py에다가

from django.urls import path

from profileapp.views import ProfileCreateView

app_name = 'profileapp'

urlpatterns = [
    path('create/', ProfileCreateView.as_view(), name='create')
]

이거 해주면 일단 접속은 돼

이렇게 나와

 

근데 일단 이 페이지로 들어갈 수 있는 링크가 있어야겠지?

그래서 accountapp/detail.html로 들어가서

 

<div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
        <p>
            {{ target_user.date_joined }}
        </p>
        
        
        <h2>
            {{ target_user.username }}
        </h2>

        {% 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>
                Delete account
            </p>
        </a>
        {% endif %}
    </div>
</div>

{% endblock %}

이거를

 

<div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
        <p>
            {{ target_user.date_joined }}
        </p>
        {% if target_user.profile %}
        <h2>
            {{ target_user.profile.nickname }}
        </h2>
        {% else %}
        <a href="{% url 'profileapp:create' %}">
            Create Profile
        </a>
        {% endif %}
        {% if target_user == user %}
        <a href="{% url 'accountapp:update' pk=user.pk %}">
            <p>
                Change Info
            </p>
        </a>

 

이런식으로 추가해주면 돼

 

Create Profile이 생겼어

 

근데 올려보면 에러가 뜨거든?

 

왜 뜨냐면...

<div style="text-align:center; max-width:500px; margin:4rem auto">
    <div class="mb-4">
        <h4>Profile Create</h4>
    </div>

    <form action="{% url 'profileapp:create' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
<!--        {{ form }}-->
        {% bootstrap_form form %}
        <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
    </form>
</div>

여기서 enctype 이게 있어야만 정상적으로 이미지 파일을 받을 수 있어

이게 없으면 아예 받지도 못하는거야

 

그 다음에는 또 에러가 떠

모야~ ㅋㅋ

 

profile에 user id가 없대

왜 이런 에러가 뜸?

 

모델에서 user란 필드가 있는데, form에서는 user란 필드를 입력 안받자나. 이미지 닉네임 메시지만 받고

이러면, 우리가 남의 프로파일을 만들어줄 가능성이 있어

그니까 클라이언트에서 이미지의 주인이라고 구라쳐서 올릴 수도 있자나

그걸 방지하기 위해서 우리는 서버단에서 처리하려고 함

 

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):
        return super().form_valid(form)

form_valid 요거, 지금 저거까지는 원래랑 똑같은 함수야

커스터마이징을 하려면 이제 바꿔줘야돼 뭔가를.

 

profileapp의 forms.py에 있는 form이 form_valid로 날아옴

 

form.save(commit=False) 이거는... 데이터베이스에 저장되는건 아니고 임시 저장되는거

 

temp_profile.user을 지금 화면을 보는 유저로 해줄거임 (self.request.user)

그리고 그걸 최종 저장하는게 temp_profile.save()

 

def form_valid(self, form):
    temp_profile = form.save(commit=False)
    temp_profile.user = self.request.user
    temp_profile.save()
    return super().form_valid(form)

이러면 CreateView의 안에 있는 form_valid에 결과를 return해주는거임

 

글고 사진 업로드 해보니까 정상적으로 됨 이제

 

글고 이제 이름이 아이디에서 닉네임으로 바뀜..!

 

profileapp에서 urls.py

app_name = 'profileapp'

urlpatterns = [
    path('create/', ProfileCreateView.as_view(), name='create'),
    path('update/<int:pk>', ProfileUpdateView.as_view(), name='update')
]

 

profileapp/templates/profileapp/에서 update.html

{% 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_profile.pk %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
<!--        {{ form }}-->
        {% bootstrap_form form %}
        <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
    </form>
</div>

{% endblock %}

 글고 accountapp/templates/accountapp/detail.html

{% if target_user.profile %}
<h2>
    {{ target_user.profile.nickname }}
    <a href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
    edit
    </a>
</h2>

추가

<div style="text-align: center; max-width: 500px; margin: 4rem auto;">
    <p>
        {{ target_user.date_joined }}
    </p>

    <img src="{{ target_user.profile.image.url }}" alt="">

    {% if target_user.profile %}
    <h2>
        {{ target_user.profile.nickname }}
        <a href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
        edit
        </a>
    </h2>

이미지 추가

 

근데 아직도 화면 안나와. 왜?

라우팅 해줘야돼

 

pragmatic/urls.py

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include

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)

 

이렇게 하면 이미지가 나옴

여기 보면 media에 사진 파일들이 올라가있어... 먼가 감동임!

 

profileapp/views.py

class ProfileCreateView(CreateView):
    model = Profile
    context_object_name = 'target_profile'
    form_class = ProfileCreationForm
    template_name = 'profileapp/create.html'

    def form_valid(self, form):
        temp_profile = form.save(commit=False)
        temp_profile.user = self.request.user
        temp_profile.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('accountapp:detail', kwargs={'pk':self.object.user.pk})

이거 보면... 원래 success url이 hello world였어

근데 그것보다 detail 페이지로 가는게 좋겠지

근데 그냥 그렇게 쓰면 에러나

그럼 얼케? 얼카냐하면, 새로운 메소드를 만들어서 오버라이딩 시켜

그래서 kwargs로 파라미터로 pk값을 넘겨주는거야.

여기서 self.object는 profile을 뜻하는거야.

 

이제 프로필 수정하면 원래 페이지로 돌아감

 

문제가 또 있음

edit이 항상 떠있어. 로그인 하든 말든.

 

일단 이 전에...

{% extends 'base.html' %}

{% block content %}

<div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
        {% if target_user.profile %}
        <img src="{{ target_user.profile.image.url }}" alt="" style="height: 12rem; width: 12rem; border-radius: 20rem; margin-bottom: 2rem;">
        <h2>
            {{ 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' %}">
                Create Profile
            </a>
            {% else %}
            닉네임 미설정
            {% 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>
                Delete account
            </p>
        </a>
        {% endif %}
    </div>
</div>

{% endblock %}

이렇게 하면, edit 이런게 로그인해서 내 아이디랑 타겟 유저 아이디가 안맞으면 안보여줘

즉, 서버단에서 처리를 해주었다!

 

과제)

1. 없는 유저 네임 번호로 접속하려고 하면 에러떠

2. 부트스트랩이 적용이 안되었는지 안예뻐

 


34강)

MagicGrid 소개 및 Articleapp 시작

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기