이제 accountapp 외에 Profileapp을 만들어본다.
1. pillow 라이브러리
profileapp 작성 전, pillow 라는 라이브러리를 설치한다. 'pip install pillow'
파이썬의 pillow 라이브러리는 이미지 처리 라이브러리다. 이미지 포맷 변환, 이미지 파일 읽고 쓰기, 이미지 편집 등의 기능을 제공한다. 나중에 이미지 처리가 필요한 부분에서 이 라이브러리를 사용할 것이다.
2. ProfileApp 시작
profileApp 생성
accountapp을 만들었을 때와 마찬가지로 profileapp을 만든다.
1. profileapp 생성 : 'python manage.py startapp profileapp' 명령어 입력
2. INSTALLED_APPS 등록 : pragmatic/settings.py의 INSTALLED_APPS에 'profileapp' 추가
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'accountapp',
'bootstrap4',
'profileapp'
]
3. url 추가 : pragmatic/urls.py의 urlpatterns에 url 추가
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accountapp.urls')),
path('profiles/', include('profileapp.urls')),
]
4. profileapp/urls.py 생성 후 app_name 및 urlpatterns 생성
app_name = 'profileapp'
urlpatterns = [
]
profile Model 생성
account Model과 마찬가지로 Profile Model을 만든다. 릴레이션은 1:1 관계로 만들 것이다. 다시 말해 사용자의 계정 1개당 1개의 Profile만 존재하도록 만든다.
profileapp/models.py
필드 중, user 필드는 User Model과의 관계를 정의한다. OneToOneField라는 메서드로 1:1 릴레이션을 설정해준다. 그리고 on_delete 옵션을 models.CASCADE로 두어 user가 삭제되면 관련된 model도 삭제되도록 해준다. related_name 속성은 user에서 조회 시 Profile 모델의 조회 이름을 뜻한다. 다시 말해 user.profile로 profile 모델에 접근이 가능해진다.
ImageField로 이미지 필드를 설정해준다. 여기서 upload_to속성은 이전 글에서 설정한 MEDIA_URL 뒤에 붙는 경로이다. 즉 이미지가 업로드 되면 '{BASE_DIR}/media/profile' 위치에 이미지들이 저장된다.
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=10, unique=True, null=True)
message = models.CharField(max_length=100, null=True)
Migration
이제 다음 두 명령어를 순서대로 입력하여 migration을 진행한다.
'python manage.py makemigrations'
'python manage.py migrate'
3. ModelForm
이제 ProfileCreationView를 작성한다. 이 때, form_class를 작성해주어야 하는데, UserCreationForm은 User 정보는 웹에서 기본적으로 필요한 것이라서 django에서 기본적으로 만들어준 것인 반면 Profile은 Profile을 위한 Form을 따로 작성해주어야 한다.
profileapp/views.py
class ProfileCreateView(CreateView):
model = Profile
context_object_name = 'target_profile'
form_class = ProfileCreationForm #따로 작성이 필요함
success_url = path(reverse_lazy('accountapp:hello_world'))
template_name = 'profileapp/create.html'
ModelFrorm
원래는 Form도 Model과 마찬가지로 상세 필드 정보들을 모두 작성해주어야 한다. 예를 들자면 아래와 같다.
class ProfileCreationForm(forms.Form):
image = forms.ImageField(upload_to='profile/', mull=True)
nickname = forms.CharField(max_length=20)
...
그런데 이렇게하면 필드가 수십 개 이상이면 너무 번거로운 작업이 된다. 따라서 django에서는 ModelForm이란 것을 제공한다. 아래 코드처럼 작성하면 된다.
profileapp/forms.py(새로 생성)
from django.forms import ModelForm
from profileapp.models import Profile
class ProfileCreationForm(ModelForm):
class Meta:
model = Profile
fields = ['image', 'nickname', 'message']
ModelForm을 상속받고, 내부 클래스로 Meta 클래스르 만들어준다. 그리고 model 및 form에서 활용하고자 하는 fields 정보를 적는다. Profile에 있는 user 필드 정보는 Form으로 클라이언트에서 받아오는 형태가 아니라 서버에서 직접 처리하는 방식으로 한다. 뒤에서 다시 다룰 것이다.
4. Profileapp 구현
이제 profileapp을 상세히 구현한다.
profileapp/templates/profileapp/create.html
디렉토리를 소제목처럼 잘 구현하자. create.html은 accountapp의 것을 복사해와서 약간만 수정한다. 다만 유의할 점은, image 파일을 업로드하는 방식이기 때문에 단순 form 태그가 아니라 form의 enctype을 multipart/form-data로 변경해주어야 한다는 것이다.
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div style="text-align: center; max-width: 500px; margin: 4rem auto">
<div class="mb-4">
<h4>Create Profile</h4>
</div>
<form action="{% url 'profileapp:create' %}" method="post" enctype="multpart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
</form>
</div>
{% endblock %}
urls.py에 등록한다.
app_name = 'profileapp'
urlpatterns = [
path('create/', ProfileCreateView.as_view(), name='create'),
]
detail view에서 접근가능하도록 해준다.
accountapp/detail.html
기존 target_user.username을 표시하던 부분을, profile.nickname으로 변경하였다. if문을 사용하여 profile 정보가 있을 때만 nickname이 표시되도록 하고, 아니면 Create Profile에 링크를 걸어 생성페이지에 접근 가능하도록했다.
{% block content %}
<div style="text-align: center; max-width: 500px; margin: 4rem auto">
{% if target_user.profile %}
<p>
{{ target_user.profile.nickname }}
</p>
{% else %}
<a href="{% url 'profileapp:create' %}">
<h2 style="font-family: 'NanumSquareB'">
Create Profile
</h2>
</a>
{% endif %}
user 필드 매칭 : 무결성 검증 해결
정보를 입력한 뒤에 profile을 생성할려고하면 아래 사진처럼 무결성 에러가 발생한다. Profile 객체를 생성하는데 연관 객체인 user 정보가 설정되어있지 않기 때문이다.
강의에서는 ProfileCreateView의 부모인 CreateView에서 제공하는 form_valid 메서드를 오버라이딩하라고 알려준다.
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):
temp_profile = form.save(commit=False)
temp_profile.user = self.request.user
temp_profile.save()
return super().form_valid(form)
자세한 원리 등은 추후에 알아보는게 좋을 것 같다. 대략적인 로직은 인자로 받아오는 form을 save하는데 commit=False로 두어 DB에 save하는 것이 아니라 임시로 저장해놓고, 로그인 유저의 User 객체(request.user)를 추가해준 뒤 save하는 방식이다.
이제 다시 업로드를 해보면 DB에도 정상적으로 저장이 되고, media/profile에 이미지 파일들이 올라간 것을 확인할 수 있다.
참조
1. 작정하고 장고! Django로 Pinterest 따라만들기 : 바닥부터 배포까지-박형석님 인프런 강의
'Programming-[Backend] > Django' 카테고리의 다른 글
Django로 Pinterest 따라 만들기-11. MagicGrid 활용, ArticleApp CRUD 완성하기 (0) | 2022.06.10 |
---|---|
Django로 Pinterest 따라 만들기-10. Profileapp 마무리, 리팩토링 (0) | 2022.06.10 |
Django로 Pinterest 따라 만들기-8. Authentication, Decorator 활용, superuser 및 media 설정 (0) | 2022.06.09 |
Django로 Pinterest 따라 만들기-7. Update, DeleteView 구현 (0) | 2022.06.09 |
Django로 Pinterest 따라 만들기-6. BootStrap 추가, DetailView 구현 (0) | 2022.06.08 |