본문 바로가기
WEB/django

[django] 이미지 파일 업로드 기능 추가

by 조창대 2022. 11. 9.
반응형

장고로 게시판을 만들다 보면 이미지를 업로드해야 할 필요성이 생긴다.

게시판이 아니더라도 웹사이트의 대부분에서 이미지 파일이 여러 용도로 쓰이기 때문에 장고로 파일 업로드 하는 걸 알아 두면 좋을 것이다. 

 

이미지 파일 업로드 기능을 구현하기 전 제품 목록 화면과 제품 코드를 클릭했을 때 나오는 제품 상세 화면이다.

 

원래 제품 목록
원래 제품 상세

 

여기서 이미지 파일 업로드 기능을 구현하고, 제품 목록 화면의 테이블에 '이미지 유무' 컬럼을 추가하고 제품 상세 화면에 이미지를 띄워보자.

 

 


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 태그를 이용해 이미지를 화면에 보여준다.

 

그럼 제품 상세 화면에 이런 식으로 첨부한 이미지가 잘 출력된다.

 

 

그럼 이미지 파일 업로드 기능 구현은 일단 끝 !

 


우리에겐 제품 목록 화면에 이미지 유무 컬럼을 표시하는 게 남아있으므로 마저 진행해보자...

 

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 태그의 클래스로 쓰여져 있는 것들은 부트스트랩임

 

그럼 이렇게 제품 목록에서 미리 제품 이미지가 첨부되어 있는지 확인할 수 있다.

 

반응형