장고로 게시판을 만들다 보면 이미지를 업로드해야 할 필요성이 생긴다.
게시판이 아니더라도 웹사이트의 대부분에서 이미지 파일이 여러 용도로 쓰이기 때문에 장고로 파일 업로드 하는 걸 알아 두면 좋을 것이다.
이미지 파일 업로드 기능을 구현하기 전 제품 목록 화면과 제품 코드를 클릭했을 때 나오는 제품 상세 화면이다.
여기서 이미지 파일 업로드 기능을 구현하고, 제품 목록 화면의 테이블에 '이미지 유무' 컬럼을 추가하고 제품 상세 화면에 이미지를 띄워보자.
1. config/settings.py
from pathlib import Path
import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
config 디렉터리의 settings.py 파일에 Path, os를 임포트하고 미디어 경로를 추가한다.
os.path.join(dir_path, file name) 은 디렉터리 경로에 파일 이름을 조합하고, path 결과를 리턴하는 함수이다.
이렇게 작성한 path는 urls.py 파일에 미디어 url 등록할 때 사용한다.
2. config/urls.py
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sales.urls')),
path('common/', include('common.urls')),
path('', views.map, name='map'), # / 페이지에 해당하는 urlpatterns
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # media 경로 추가
static과 settings를 임포트해주고, 기존의 urlpatterns에 settings.py에서 정의한 미디어 경로를 추가한다.
이렇게 코드를 추가함으로써 이미지는 테이블 속성 값으로 이미지 자체로 저장되어있는 것이 아닌 '/media/이미지이름.png' 처럼 path가 저장된다.
맨 아래 코드가 추가 코드임.
3. python -m pip install pillow
cmd 창에서 python -m pip install pillow 명령어로 이미지 파일 업로드에 필요한 모듈을 설치한다. 난 이미 설치해서 업데이트하라는 문구가 뜬다.
가상환경에서 개발을 진행하고 있다면 꼭! 가상환경으로 진입한 후 설치를 진행한다.
4. 앱 이름/models.py
class Product(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE) # User는 django.contrib.auth.models가 제공하는 모델
modify_date = models.DateTimeField(null=True, blank=True)
pcode = models.CharField(max_length=10)
pname = models.TextField()
unitprice = models.IntegerField(default=0)
discountrate = models.DecimalField(max_digits=11, decimal_places=2,default=0)
mainfunc = models.CharField(max_length=100, default="")
imgfile = models.ImageField(null=True, upload_to="", blank=True) # 이미지 컬럼 추가
detailfunc = models.CharField(max_length=200, default="")
업로드한 이미지 정보를 저장하고 싶은 모델에 imgfile 컬럼을 추가한다.
5. makemigrations, migrate
model에 대한 변경사항이 있을 땐 항상 cmd에서 makemigrations, migrate 해주는 거 잊지 말기.
6. 앱 이름/forms.py
class ProductForm(forms.ModelForm): # ModelForm 은 장고 모델 폼
class Meta: # 장고 모델 폼은 반드시 내부에 Meta 클래스 가져야 함
model = Product
fields = ['pcode', 'pname', 'unitprice', 'discountrate', 'mainfunc', 'detailfunc', 'imgfile']
labels = {
'pcode': '제품 코드 ',
'pname': '제 품 명 ',
'unitprice': '단 가 ',
'discountrate': '할 인 율 ',
'mainfunc': '주요 기능',
'detailfunc': '상세 기능',
'imgfile': '이 미 지'
}
기존 폼에 이미지 항목을 추가한다.
7. 앱 이름/views.py
def product_create(request):
if request.method == 'POST':
form = ProductForm(request.POST, request.FILES) # 꼭 !!!! files는 따로 request의 FILES로 속성을 지정해줘야 함
if form.is_valid():
product = form.save(commit=False)
product.author = request.user
product.detailfunc = product.detailfunc.replace("'", "").replace("[", "").replace("]", "")
product.save()
return redirect('sales:index')
else:
form = ProductForm() # request.method 가 'GET'인 경우
context = {'form':form}
return render(request, 'sales/product_form.html', context)
기존에는 뷰 함수에서 ProductForm 으로부터 테이블 정보를 request.POST 방식으로 받았지만,
파일 정보는 request.FILES 방식으로 받기 때문에 이를 추가해준다.
product_create 함수는 제품 정보를 생성할 때 템플릿으로 입력받은 값을 전달받고 정보들을 테이블에 전달하는 함수이다.
product_create 와 연결된 템플릿인 product_form.html도 수정해주자.
8. templates/product_form.html
<div class="container">
<h5 class="my-3 border-bottom pb-2">키오스크 등록</h5>
<form method="post" class="post-form my-3" enctype="multipart/form-data">
<div class="form-group">
<label for="imgfile">제품사진 : </label>
<input type="file" class="form-control" name="imgfile" id="imgfile">
</div>
매우 중요한 대목임.
나는 저 from 태그에 enctype="multipart/form-data" 속성 넣는 걸 까먹어서 며칠 헤맸다. 왜 할 거 다 했는데 이미지 등록 버튼을 클릭해도 반응이 없을까 하고.
input 태그는 이미지 파일을 사용자로부터 받는다.
9. media 디렉터리
제품 등록할 때 이미지 파일 등록하고, 폼 제출까지 완료하면 이렇게 media 디렉터리에 이미지 파일이 저장되는 걸 확인할 수 있다.
10. templates/product_detail.html
{% if product.imgfile %}
<div class="container border col-10">
<div style="width:10%; height:150px; float:left; margin-right:10px;">
<img src="{{ product.imgfile.url }}" width="700">
</div>
{% endif %}
if문을 통해 imgfile 이 있을 경우, img 태그를 이용해 이미지를 화면에 보여준다.
(url 속성은 상대경로(MEDIA_ROOT부터 시작하는 경로)를, path 속성은 절대경로를 나타냄)
그럼 제품 상세 화면에 이런 식으로 첨부한 이미지가 잘 출력된다.
그럼 이미지 파일 업로드 기능 구현은 일단 끝 !
우리에겐 제품 목록 화면에 이미지 유무 컬럼을 표시하는 게 남아있으므로 마저 진행해보자...
11. templates/product_list.html
<div class="container my-3">
<table class="table">
<thead>
<tr class="text-center thead-light">
<th>번호</th>
<th>제품코드</th>
<th>제품명</th>
<th>단가</th>
<th>할인율</th>
<th width="200" height="50">주요기능</th>
<th width="120" height="50" align="center" valign="top" style="padding-top:15px;">이미지 유무</th>
<th>작성자</th>
</tr>
...
<td width="120" height="50" align="center" valign="top" style="padding-top:15px;">
{% if product.imgfile %}
<span class="badge badge-secondary py-1">
◯
</span>
{% else %}
<span class="badge badge-warning py-1">
✕
</span>
{% endif %}
</td>
th 태그로 이미지 유무 컬럼을 추가하고,
td 태그에는 제품의 이미지가 있을 경우와 없을 경우를 {% if product.imgfile %} 로 가려냈다.있을 경우 동글뱅이, 없을 경우 엑스 표시. 참고로 span 태그의 클래스로 쓰여져 있는 것들은 부트스트랩임
그럼 이렇게 제품 목록에서 미리 제품 이미지가 첨부되어 있는지 확인할 수 있다.
'WEB > django' 카테고리의 다른 글
[django] 장고 form select option 값 뷰 함수에서 GET으로 받기 (1) | 2022.11.01 |
---|