Django

장고(Django) - media 파일 다루기

티베트 모래여우 2021. 3. 23. 01:28
반응형

서론

장고에서의 media 파일은 ImageField와 FileField를 통해 저장하게 된 파일들을 의미합니다.

단순히 저 두가지 필드만을 통해 저장할 수 있는 것은 아니고 FileField를 상속받아 커스텀 필드를 만들어 저장하는 것도 가능합니다.(ImageField도 내부적으로는 FileField를 상속받고 있습니다.)

다만 필드를 통해 media 파일을 저장한다고 해서 필드에 파일이 직접 저장되는 것은 아닙니다. 필드는 단지 media 파일이 저장된 경로를 저장하고 있을 뿐입니다.

Settings.py

media 파일을 다루기 위해선 settings.py에서 몇 가지 설정을 해 주어야 합니다.

1. MEDIA_URL 설정

# settings.py
MEDIA_URL = "/media/"

settings.py에 MEDIA_URL이라는 값을 추가해 줍니다.

이 값은 각 media파일에 접근할 때 접두사(prefix)의 역할을 하게 됩니다. 필드의 url속성을 통해 상대경로로 접근할 때도 볼 수 있습니다.

예시: "/media/photo1.png"

(예제처럼 꼭 media로 할 필요는 없습니다.)

2. MEDIA_ROOT 설정

# settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

다음으로 MEDIA_ROOT값을 추가해 줍니다.

이 값은 실제로 파일이 저장되는 경로를 설정하는 값입니다.

기본적으로 BASE_DIR에는 manage.py가 위치한 경로가 지정되어 있습니다. 그 하위경로에 media라는 이름의 디렉토리를 생성해 그 곳에 실제로 media 파일들을 저장하게 됩니다.(경로를 저장하는게 아닙니다.)

3. (개발환경 한정) url 지정

# settings.py 경로의 urls.py
from django.conf.urls.static import static
...
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

runserver 명령을 통해 구동하는 개발 서버에서는 media 파일을 자동으로 서빙해 주지 않습니다. 따라서 수동으로 urlpattern을 추가함으로서 서빙해야합니다.

이 설정을 추가하면 MEDIA_URL로 들어오는 요청에 대해 MEDIA_ROOT 경로를 탐색하게 됩니다.

* 사족으로, 위 코드에서 사용한 static 함수는 settings.py의 DEBUG가 false일 경우 빈 리스트를 반환하게 되어있습니다. 다만 좀 더 명시적으로 표시하기 위해 if문을 넣었습니다.

4. pillow 패키지 설치

ImageField가 pillow 패키지에 대해 의존성을 가지기 때문에 pillow 패키지를 설치해 주어야 합니다.

pip install로 pillow를 설치합시다.

media 파일 저장하기

모든 준비가 끝났다면 media 파일을 저장할 수 있습니다.

# models.py
photo = models.ImageField(upload_to="")

이렇게 상기한 방법으로 model에서 field를 지정해 저장할 수 있습니다.

저장하고 migrate까지 끝내면 admin 화면에서 이렇게 보이게 됩니다.

필드 옵션에 upload_to 라는 옵션을 주었는데 이 옵션은 파일이 저장되는 중간 경로를 설정할 수 있는 옵션입니다.

즉 upload_to 옵션을 통해 /media/upload_to에서 지정한 경로/파일명.확장자 의 형태로 파일을 저장할 수 있습니다. 파일이 적다면 문제될 게 없으나 한 디렉토리에 파일의 양이 많아질 경우 성능저하를 일으키게 되므로 해당 옵션을 잘 활용하여 세분화 시켜서 저장하는 것이 좋습니다. (디렉토리의 depth는 성능에 크게 영향이 없습니다.)

해당 옵션은 문자열과 함수로 값을 지정할 수 있는데 두 경우에 따른 차이가 살짝 있습니다.

photo = models.ImageField(blank=True, upload_to="/photo/%Y/%m/%d")
# /media/photo/년/월/일/파일명.확장자

문자열로 지정시 중간 경로만 설정 가능합니다.

def upload_to_func(instance, filename):
    prefix = timezone.now().strftime("%Y/%m/%d")
    file_name = uuid4().hex
    extension = os.path.splitext(filename)[-1].lower() # 확장자 추출
    return "/".join(
        [prefix, file_name, extension,]
    )
photo = models.ImageField(blank=True, upload_to=upload_to_func)

함수로 지정시 중간 경로를 포함하여 파일명까지 설정 가능합니다.

위의 예제는 파일명을 uuid값으로 바꿔 저장하는 예제입니다.

* 확장자 추출시에 사용하는 splitext 함수는 요소가 2개인 튜플을 리턴하는데 첫 요소에는 확장자를 제외한 경로가, 두 번째 요소에는 확장자가 저장되어 있습니다. 따라서 인덱싱을 [-1]이 아니라 [1]로 그냥 하셔도 무방합니다.

 

<form action="" enctype="multipart/form-data" method="POST">
...
</form>

실제로 form을 통해 media 파일을 저장할때는 method 타입을 POST로, enctype을 multipart로 지정해 주어야 정상적으로 파일이 넘어오게 됩니다.

# views.py
...
def home_view(request):
    if request.method == "POST":
        print(request.FILES["photo"])
    return render(request, "test/home.html")

form으로 넘어온 파일은 request 객체의 FILES를 통해 접근할 수 있습니다. 이 때 사용한 키 값(예제에서는 photo)은 form에서 지정된 name으로 사용합니다.

media 파일 사용하기

<img src={post.photo.url} style="width:100px; height:100px" />
<img src={post.photo.path} style="width:100px; height:100px" />

이렇게 저장된 media 파일은 생성된 인스턴스.필드명.url 혹은 path 속성을 통해 사용할 수 있습니다.

어느 쪽을 사용해도 무방하지만 url 속성은 상대경로(MEDIA_ROOT부터 시작하는 경로)를, path 속성은 절대경로를 나타낸다는 점만 기억하시면 됩니다.

반응형