(Django) Media file 다루기

duo2208·2021년 9월 1일
4

Django

목록 보기
4/23
post-thumbnail
post-custom-banner

Static & Media 파일

1. Static

  • 개발 리소스로서의 정적인 파일 (js, css, image 등)
  • 앱 / 프로젝트 단위로 저장 및 서빙

2. Media

  • 유저가 업로드 한 모든 정적인 파일 (image, pdf)
    관리자가 admin 페이지에서 올리는 파일도 포함이다.
  • FileField/ImageField를 통해 저장한 모든 파일
  • 💡 DB필드에는 저장경로(문자열)를 저장하며, 파일은 File Storage에 저장된다.
  • 💡 프로젝트 단위로 저장 및 서빙

+ Media 파일 처리 순서

(1) HttpRequest.FILES를 통해 파일 전달
(2) 뷰 로직이나 폼 로직을 통해 유효성 검증을 수행하고,
(3) FileFiled/ImageField 필드에 "경로(문자열)"를 저장하고,
(4) settings.MEDIA_ROOT 경로에 파일을 저장한다.


Media 파일 경로 설정

settings.py

MEDIA_URL : 각 media 파일에 대한 URL Prefix. 필드명. url 속성에 의해서 참조되는 설정.
MEDIA_ROOT : 파일필드를 통한 저장 시에, 실제 파일을 저장할 ROOT 경로.

...
# BASE_DIR은 base.py의 위치에 따라 결정되기 때문에 
# 운영 서버 또는 개발 머신 어디서나 프로젝트를 구동할 수 있음.
# 따라서 파일 경로 관련해서는 절대 하드코딩을 하지말자.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
...

MEDIA_URL = '/media/'		# ex) /media/photo1.png
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

urls.py (개발환경 한정)

django에서는 static 파일과 다르게 runserver 를 통한 개발 환경에서의 media 파일 서빙을 권장하지 않는다. 개발 편의성 목적으로 직접 서빙하고자 한다면, 수동으로 urlpattern을 추가하여야 한다.

from django.conf.urls.static import static
from django.conf import settings

# MEDIA_URL로 들어오는 요청에 대해 MEDIA_ROOT 경로를 탐색한다.
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

만약 settings.DEBUG 옵션을 끄면 static 함수는 빈 리스트를 반환한다. 없어도 충분한 옵션이지만 명시적인 표현을 위해 if문이 사용되었다.

🚀 (Django) serving static files during development


FileField & ImageFiled (Using files in models)

1. FileField

  • 파일 저장을 지원하는 모델 필드
  • File Storage API를 통해 파일을 저장한다.
  • 해당 필드를 옵션 필드로 두고자 할 경우 blank=True 를 사용하자.

2. ImageField (FileField 상속)

  • 이미지 저장을 지원하는 모델 필드
  • pillow 를 통해 이미지 width와 height를 흭득한다. pillow 미설치 시,
    ImageField를 추가한 makemigrations 수행에 실패한다.
$ pip install pillow
🚀 (Django) Using files in models


효율적인 파일 경로 / 커스텀

⚡ 한 디렉토리에 파일을 너무 많이 몰아 둘 경우 파일찾기 성능이 저하된다. 디렉터리 Depth가 깊어지는 것은 성능에 큰 영향이 없으므로, 하위 디렉터리를 만들어 파일들을 관리하자.

+ upload_to 필드 옵션을 통하여 다른 디렉터리 저장경로 가지기

(1) 필드 별로 다른 디렉터리에 저장
(2) 업로드 시간대 별로 다른 디렉터리에 저장

models.py

from django.db import models

class POST(models.Model):
	# (1) 저장경로 : MEDIA_ROOT/blog/post/xxx.png
    	#     DB필드 : MEDIA_URL/blog/post/xxx.png' 문자열 저장
	photo = models.ImageField(blank=True, upload_to='blog/post')
    
	# (2) 저장경로 : MEDIA_ROOT/blog/post/20210901/xxx.png
    	#     DB필드 :  'MEDIA_URL/blog/post/20210901/xxx.png' 문자열 저장
	photo = models.ImageField(blank=True, upload_to='blog/post/%Y%m%d')   
🚀 (Django) FileField.upload_to


Media 실습

models.py

from django.conf import settings
from django.db import models

class POST(models.Model):
    message = models.TextField(blank=True)
    # ex) WOW.png 파일을 업로드할 경우, 
    # instagram/post/20210901/WOW.png 경로에 저장되며
    # DB에는 "WOW.png" 문자열을 저장한다.
    photo = models.ImageField(blank=True, upload_to='instagram/post/%Y%m%d')   
    is_public = models.BooleanField(default=False, verbose_name='공개여부') 
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        # return f"Custom Post object ({self.id})"
        return self.message

admin.py

from django.contrib import admin
from django.utils.safestring import mark_safe
from .models import POST

@admin.register(POST)
class POSTAdmin(admin.ModelAdmin):
    list_display = ['id', 'photo_tag', 'message', 'message_length', 'is_public', 'created_at', 'updated_at']
    list_display_links = ['message']
    list_filter = ['created_at', 'is_public']
    search_fields = ['message']

    def photo_tag(self, post):
     	# 안전하게 필드명 저장유무를 체크해주자.
        # 필드에 저장된 경로가 없을 경우, url 계산에 실패한다.
        if post.photo: 
            # 보안으로 escape 처리가 되어 marksafe를 사용해야함.
            return mark_safe(f'<img src="{post.photo.url}" style="width:50px;" />')
        return None

    def message_length(self, post):
        return len(post.message)
    message_length.short_description = "메시지 글자 수"

결과

결과1

결과2



📌 참고 출처

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 2월 6일

감사합니다.

답글 달기