1. Static
개발 리소스
로서의 정적인 파일 (js, css, image 등)2. Media
업로드
한 모든 정적인 파일 (image, pdf)(1) HttpRequest.FILES를 통해 파일 전달
(2) 뷰 로직이나 폼 로직을 통해 유효성 검증을 수행하고,
(3) FileFiled/ImageField 필드에 "경로(문자열)"를 저장하고,
(4)settings.MEDIA_ROOT
경로에 파일을 저장한다.
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문이 사용되었다.
1. FileField
blank=True
를 사용하자.2. ImageField (FileField 상속)
pillow
를 통해 이미지 width와 height를 흭득한다. pillow
미설치 시,$ pip install pillow
⚡ 한 디렉토리에 파일을 너무 많이 몰아 둘 경우 파일찾기 성능이 저하된다. 디렉터리 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')
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 = "메시지 글자 수"
결과
감사합니다.