본문 바로가기
Python/Django

django - 이미지 파일 업로드하고 html 불러오기

by zenna 2022. 4. 21.
728x90

html로 구현된 화면에서 이미지를 업로드하면
django를 통해 지정된 폴더에 이미지가 저장되고
저장된 주소는 MySQL에도 저장되도록 제작했습니다. 
설명을 이해하기 쉽도록 변수는 한글로 기재하지만
실제 프로그래밍 시에는 영어로 된 변수를 설정해주셔야 합니다.

 

 

참고한 사이트

 

Django에서 파일 업로드

Django에서 파일을 업로드하는 방법 알아보기

www.delftstack.com

 

진행 전, 가상환경에는 Pillow가 설치되어 있어야 합니다. 

터미널에서 pip install Pillow를 실행하여 설치할 수 있습니다. 

장고와 MySQL의 기본 빌드는 이미 구성된 상태임을 전제로 합니다. 

 

참고 사이트에서는 Core라는 앱을 하나 생성해서 사용했지만

저는 기능별로 앱을 구분해서 사용했습니다.

따라서 아래 두 개의 앱을 구분해서 보셔도 되고

한 앱에서 모든 기능을 구현하시는 분들은 

앱1과 앱2를 동일하게 보시면 됩니다!

 

models.py가 선언된 앱 = 앱이름1

데이터를 읽고 쓸 앱 = 앱이름2

라고 하겠습니다. 


settings.py 의 맨 밑에 있는 MEDIA_URL과 MEDIA_ROOT를 수정해줍니다. 

import os 

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

1. MEDIA_URL : 미디어 파일의 url을 설정

2. MEDIA_ROOT : 미디어 파일의 저장 경로를 정의


이미지 파일이 저장된 경로를 저장할 MySQL 테이블을 만들어 주겠습니다. 

저는 설명이 이해되기 쉽도록 한글 이름으로 적었지만 아래 한글 변수들은 모두 프로그래밍 시 영문으로 작성해주셔야 합니다.

앱이름/models.py

from django.db import models

class 테이블(models.Model) :
    컬럼_파일위치 = models.FileField(upload_to='Uploaded Files/%y/%m/%d/', blank=True)
    컬럼_업로드날짜 = models.DateField(auto_now = True)

파일 저장 위치는 '프로젝트 폴더/media/년/월/일' 로 하겠습니다.

(%/y/%m/%d 가 year, month, date를 의미해요)

파일이 많으면 프로그램이 찾는 데 오래걸리지만

이렇게 하위 폴더를 여러번 생성해서 파일을 저장하면

사용 시 효율적입니다.

 

이렇게 선언해주고 터미널에서 

python manage.py makemigrations
python manage.py migrate

를 실행하면

이런 테이블이 생성됩니다. 

여기서 id는 자동으로 생성되고, 데이터가 추가될때마다 하나씩 증가하는 int 예요.

 


이제 사람들이 파일을 업로드 할 수 있도록 html 화면 구성을 해줍니다.

저는 아래와 같은 모양으로 만들거예요

그렇지만 CSS 요소는 다 빼고 꼭 필요한 부분만 추려서 코드 설명할게요!

html 위치는 "프로젝트폴더/앱이름2/templates" 입니다. 

이 html은 이제 폼.html 이라고 할게요

<div>
    데이터를 업로드하세요! <br>
    <form action="{% url '앱이름2 : '이름_파일업로드' %} " method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="file" name="인풋_파일업로드">
        <input type="submit" value="등록하기">
    </form>
</div>

1. form action : 폼이 제출(submit)되면 이동할 url을 의미합니다.

  아직까지 {% url '앱이름2 : 펑션_파일업로드 %}는 이제 작성할 views에서의 function입니다. 

2. method="POST" : 폼의 전송 방식은 POST와 GET 두 가지가 있는데, 여기서는 파일을 보내기 위해 POST를 사용합니다. 

3. {% csrf_token %} : 폼을 POST로 보내기 위해 필수적으로 필요한 보안 요소예요. 폼 중 아무곳에나 위치해도 상관없어요.

4. <input type="file"> : 파일선택하는 버튼과 선택된 파일 이름이 보이는 부분입니다. 

5. <input type="submit"> 폼을 제출하는 버튼이에요. <button> 과는 약간 달라요.

 

그리고 폼을 제출하고 나서 등록되어있는 사진이 모두 뜰 페이지도 만들어줄게요.

결과.html이라고 하겠습니다~

    <table>
        <tr>
            <th>ID</th>
            <th>File Path</th>
            <th>Upload Date & Time</th>
        </tr>
        {% for 변수_222 in 키_테이블의모든정보 %}
            <tr>
                <td>{{ 변수_222.id }}</td>
                <td>{{ 변수_222.컬럼_파일위치 }}</td>
                <td>{{ 변수_222.컬럼_업로드날 }}</td>
            </tr>
        {% endfor %}
    </table>

이제 폼이 제출되면 수행할 동작을 지정해 줄 거예요. 

"프로젝트폴더/앱이름2/views.py"로 이동합니다.

 

폼이 실행되면 현재 url에 폼의 입력 값들이 파라미터로 달라붙을거예요.

from 앱이름1 import models
from django.shortcuts import render
from 앱이름.models import 테이블

def 펑션_파일업로드(request):
    if request.method == "POST":
    #폼에서 데이터를 받아와 변수화시키기
    
        변수_업로드파일 = request.FILES["인풋_파일업로드"]

        # 정보를 파일에 저장하기
        변수_파일저장 = models.테이블(
            컬럼_파일위치 = 변수_업로드파일
        )
        변수_파일저장.save()

    변수_테이블의모든정보 = models.테이블.objects.all()

    return render(request, "앱이름2/결과.html", context = {
        "키_테이블의모든정보": 변수_테이블의모든정보
    })

 


이제 url 과 views에서 방금 선언해준 '펑션_파일업로드'를 연결해줘야 해요

앱이름2/urls.py

from 앱이름2 import views
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static

app_name = "앱이름2"

urlpatterns = [
    path("", views.펑션_파일업로드, name = "이름_파일업로드"),
]

if settings.DEBUG: 
    urlpatterns += static(
        settings.MEDIA_URL, 
        document_root = settings.MEDIA_ROOT
    )

이제 드디어 아까 폼을 제출하면 연결되는 url에 선언한 내용이 다 나왔네요. 

{% url '앱이름2 : '이름_파일업로드' %} 는 '앱이름2'의 urls.py 파일에 선언된 많은 path중에 'name = 이름_파일업로드'인것을 찾아가도록 선언 해 준거랍니다.  

 


이제 터미널에 python manage.py runserver을 한 뒤 테스트해보시면 됩니다!

그런데 저는.. 화면에서 '모든 업로드 된 이미지들'이 아니라 제가 방금 올린 이미지만 확인하고싶어요.

코드를 약간 수정해주겠습니다.

일단 

1. 파일을 업로드하면 방금 업로드 한 파일의 컬럼 정보만 불러와야 합니다.
   object.all() 로 테이블의 모든 정보를 가져오면 안되는거죠.

2. 그래서 필터링을 하기 위해 MySQL에 파일 업로드 위치를 넣어줄 때, 이 위치를 변수화해서 url에 넣어줄거예요.

3. 넣어준 url을 처리할 두번째 function을 선언해주면

4. 'url을 통해 변수화되어 넘어온 파일 업로드 위치' = 'MySQL 테이블의 컬럼_파일위치' 인 컬럼만

   걸러 화면에 표시해 줄 수 있겠죠?


앱이름2.views 를 수정해주겠습니다.

import도 추가되었으니 빠트린 것 없이 모두 기재했는지 반드시 확인하세요!

from django.shortcuts import render, get_object_or_404
from 앱이름1 import models
from 앱이름1.models import 테이블
from django.http import HttpResponseRedirect
from django.urls import reverse

def 펑션_파일업로드(request):
    if request.method == "POST":
    #폼에서 데이터를 받아와 변수화시키기
        변수_업로드파일 = request.FILES["인풋_파일업로드"]

        # 정보를 파일에 저장하기
        변수_파일저장용 = models.테이블(
            변수_파일업로드 = 인풋_파일업로드
        )
        변수_업로드파일.save()
        변수_파일업로드위치=변수_파일저장용.컬럼_파일위치

    변수_해당컬럼 = get_object_or_404(테이블, 컬럼_파일위치 = 변수_파일업로드위치)
    변수_해당컬럼_id = 변수_해당컬럼.id
    return HttpResponseRedirect(reverse('앱이름2:펑션_화면표시', args=(변수_해당컬럼_id,)))
    
    
    def 펑션_화면표시(request, 변수_해당컬럼_id):
    변수_해당컬럼 = get_object_or_404(테이블, id= 변수_해당컬럼_id)
    context = {'키_해당컬럼': 변수_해당컬럼}
    return render(request, '결과.html', context)

 

 

urls 수정, 리다이렉트 된 path를 추가해줍니다. url에 int타입 id를 같이 보내줬으니까 인식하도록 해줘야해요.

from 앱이름2 import views
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static

app_name = "앱이름2"

urlpatterns = [
    path("", views.펑션_파일업로드, name = "이름_파일업로드"),
    path("<int: 변수_해당컬럼_id >/", views.펑션_화면표시, name='펑션_화면표시')
]

if settings.DEBUG: 
    urlpatterns += static(
        settings.MEDIA_URL, 
        document_root = settings.MEDIA_ROOT
    )

 

폼을 제출하면 이동하는 html화면도 수정해서 해당되는 사진만 뜨도록 바꿔줍니다. 

<div>
    <img src= "/media/{{키_해당컬럼.uploadedFile}}" /> 
    {{oiai.uploadedFile}}
</div>

완성~!!!

아래 접힌 페이지를 확인하시면 모든 완성된 코드와 파일 경로가 기재되어 있습니다. 

 

728x90

댓글