파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.
장고의 CharField는 validators를 지정해서 입력을 보다 타이트하게 지정할 수 있다.
또한 choices인자에 TextChoices를 전달함으로서 유저가 텍스트를 선택하도록 할 수 있다. (ex. 성별: 남, 여 중 선택)
ImageField를 통해서 파일을 입력받을 수도 있다.
본 글에서는 유저의 프로필 모델을 정의해보면서 위에서 설명한 CharField의 validators, choices인자 및 ImageField를 사용해본다.
프로필 모델에 구현할 필드는 다음과 같다.
필드 | 설명 | 장고 필드 |
---|---|---|
phone_number | 휴대폰 번호 | CharField(validators=?) |
gender | 성별 | CharField(choices=?) |
avatar | 프로필 이미지 | ImageField() |
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.core.validators import RegexValidator
from django.db import models
from django.template.loader import render_to_string
class User(AbstractUser):
class GenderChoices(models.TextChoices):
MALE = 'M', '남성'
FEMALE = 'F', '여성'
# ...
phone_number = models.CharField(validators=[RegexValidator(r'010-?[1-9]\d{3}-?\d{4}$')], max_length=13, blank=True)
gender = models.CharField(choices=GenderChoices.choices, max_length=1, blank=True)
avatar = models.ImageField(blank=True, upload_to='accounts/avatar/%Y/%m/%d',
help_text='48px * 48px 크기의 png/jpg 파일을 업로드해주세요')
# ...
RegexValidator를 활용해 정규표현식을 정의하였다. 그리고 CharField의 Validators 인자에 넣어주었다.
지정한 정규표현식 규칙
blank=True
을 통해 빈 스트링이 입력 가능하도록 하였다.
phone_number = models.CharField(validators=[RegexValidator(r'010-?[1-9]\d{3}-?\d{4}$')], max_length=13, blank=True)
TextChoices를 상속받은 GenderChoices를 정의하였다.
blank=True
을 통해 빈 스트링이 입력 가능하도록 하였다.
gender = models.CharField(choices=GenderChoices.choices, max_length=1, blank=True)
ImageField는 CharField를 래핑해서 만든 필드다. 내부적으로 max_length는 설정되어 있다.
upload_to를 통해 저장 경로를 입력해준다. (upload_to에 함수를 지정하는 방법)
blank=True
을 통해 빈 스트링이 입력 가능하도록 하였다.
이미지필드는 django-imagekit이라는 라이브러리를 통해 구현할 수 있다. 다양한 유효성 검사를 지원하고 이미지 변환 또한 지원한다.
avatar = models.ImageField(blank=True, upload_to='accounts/avatar/%Y/%m/%d', help_text='48px * 48px 크기의 png/jpg 파일을 업로드해주세요')
모델에서 정의한 필드를 폼에도 추가해준다.
# accounts/forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = User
fields = ['avatar', 'phone_number', 'gender', # ...]
파일을 저장하려면 request.FILES를 폼에 입력해줘야 한다.
ProfileForm()과 같이 아무런 인자를 넣지 않고 폼을 호출하면 안된다. (빈 프로필을 생성하려고 한다.)
# accounts/views.py
@login_required
def profile_edit(request):
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
form.save()
messages.success(request, '프로필을 수정/저장했습니다.')
return redirect('profile_edit')
else:
form = ProfileForm(instance=request.user)
return render(request, 'accounts/profile_edit_form.html', {
'form': form
})
# urls
urlpatterns = [
# ...
path('edit/', views.profile_edit, name='profile_edit'),
]
askcompany/templates/layout.html
user에 avatar가 있다면, img태그의 src인자에 url을 넣어준다.
...
<a class="p-2 text-dark" href="{% url 'profile_edit' %}">
{% if user.avatar %}
<img src="{{ user.avatar.url }}" alt="">
{% else %}
<img src="{% url 'pydenticon_image' data=user.username %}" style="width: 24px; height: 24px">
{% endif %}
</a>
...
askdjango/templates/_form.html
파일 업로드를 하려면 enctype에 multipart/form-data로 지정되어 있어야 한다.
{% load bootstrap4 %}
<div class="card">
{% if form_title %}
<div class="card-header">
{{ form_title }}
</div>
{% endif %}
<div class="card-body">
{% if form %}
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{{ submit_label|default:"Submit" }}
</button>
{% endbuttons %}
</form>
{% else %}
<div class="alert alert-danger">
form 객체를 지정해주세요
</div>
{% endif %}
</div>
</div>
accounts/templates/accounts/layout.html
{% extends 'layout.html' %}
accounts/templates/accounts/profile_edit_form.html
{% extends 'accounts/layout.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-sm-6 offset-sm-3">
{% include '_form.html' with form_title='프로필 수정' submit_label='프로필 수정' %}
</div>
</div>
</div>
{% endblock %}