이전 글에서는 서버에서 준비된 정적 파일을 내보내는 static(개발자가 준비하는 파일) 에 대해서 알아보았다.
이번 글에서는 사용자가 파일을 보내고 웹에 띄울 수 있는 media(이용자들이 올리는 파일) 을 배워보자.
이전 글에서 작성했던 STATIC_ROOT, STATIC_URL을 그대로 복사/붙여넣기 한다.
그리고 static을 전부 media로 바꿔준다.
#settings.py
#맨 위에 os를 import 해주세요
import os
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
# 어떤 식으로 static 파일을 관리하는지 공식 문서로 나와있다
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'blog', 'static')
# os.path.join(BASE_DIR, '앱이름', 'static')
]
# 현재 static 파일들이 어디에 있는지
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# static django에서는 편의를 위해 흩어져있는 static파일을 한곳에 모으는데,
# 그때 파일을 모아줄 위치를 나타냅니다.
# Django 3.1 이상부터는
# MEDIA_ROOT = Path(BASE_DIR, 'media')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 이용자가 업로드한 파일을 모아두는 곳
MEDIA_URL = '/media/'
# 기존 코드
from django.contrib import admin
from django.urls import path, include
import blog.views
# 아래 코드 추가!!
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [...] # 기존 코드
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # 코드 추가!
앞서 기본적인 웹 통신에 관하여 설명했을 때, 서버와 클라이언트는 기본적으로 URL 을 통하여 통신한다는 사실이 기억날 것이다.
media와 static 역시 이를 기반으로 작동한다는 것을 인지하고 있으면 된다.
따라서 urls.py
의 내용을 위와 같이 추가한다.
settings
와 static
관련 내용을 import
하고 맨 마지막에 static 관련한 한 줄을 추가한다. 그냥 이렇게 해야 이미지를 읽어올 수 있다고 생각하면 된다. (자세한 작동원리는 공식 문서를 참고하자.)
주의할 점! : 여기서 urls.py
는 앱 내의 파일을 의미하는 것이 아니라 프로젝트 내의 urls.py
를 의미한다! 명심하자!
# models.py
from django.db import models
# Create your models here.
class Blog(models.Model):
title = models.CharField(max_length=200)
writer = models.CharField(max_length=10)
pub_date = models.DateTimeField()
body = models.TextField()
image = models.ImageField(upload_to="blog/", blank=True, null=True)
def __str__(self):
return self.title
def summary(self):
return self.body[:100]
image
를 살펴보면 upload_to='blog/'
라는 내용이 있는데 이 부분이 업로드 된 이미지들을 media 폴더 안의 blog 폴더 안에 넣으라는 의미이다.
pip install Pillow
Pillow란?
파이썬 이미징 라이브러리로서 여러 이미지 파일 포맷을 지원하고, 이미지 내부 데이터를 엑세스할 수 있게 하며, 다양한 이미지 처리 기능을 제공.
DB에 이미지를 저장하기 위해서는 Pillow를 설치하여 내부적으로 이미지를 DB에 저장할 수 있는 데이터 형식으로 변환이 필요하기에 Pillow가 필요.
이미 blog 테이블에 row가 존재한다면?
현재 저희가 Blog 테이블에 image라는 필드(컬럼)를 추가해줬습니다.
하지만 이전에 저희가 이미 데이터(row)를 집어 넣었었죠 ?
그러면 이미 넣었던 데이터(row)들은 image 컬럼에는 아무것도 들어있지 않아서 오류가 발생 할 수 있습니다
따라서 이 설정을 해줍니다
image = models.ImageField(upload_to="blog/", blank=True, null=True)
그리고 다른 방법으로는 migrations 초기화, db 삭제 하고 다시 마이그레이션을 해주시면 오류 없이 진행이 가능합니다
그 다음에 migration을 해주자.
python manage.py makemigrations
python manage.py migrate
# new.html
{% extends 'base.html' %}
{% block content %}
<h1>New Blog Entry</h1>
<form action="{% url 'blog:create'%}" method="post" enctype="multipart/form-data">
{%csrf_token%}
<p>제목: <input type="text" name="title" /></p>
<p>작성자: <input type="text" name="writer" /></p>
<p>사진: <input type="file" name="image" /></p>
본문: <br /><textarea name="body" id="" cols="30" rows="10"></textarea
><br />
<button type="submit">작성하기</button>
</form>
{% endblock %}
input 태그를 활용하여 포스트를 생성하는 request(create)를 보낼 때 이미지도 함께 보내준다. 태그들의 속성들은 구글링을 통해 알아보시는 것을 권장한다.
또한 form 태그에서 위의 코드처럼 enctype
속성을 지정하지 않는다면, views에서 request를 처리할 때 에러가 발생하니 꼭 작성하여주자!
request를 보냈으니 이제 views.py 에서도 해당 이미지를 넣도록 수정하여 주자.
#views.py
def create(request):
new_blog = Blog()
new_blog.title = request.POST['title']
new_blog.writer = request.POST['writer']
new_blog.body = request.POST['body']
new_blog.image = request.FILES['image']
new_blog.pub_date = timezone.now()
new_blog.save()
return redirect('blog:detail', new_blog.id)
{% extends 'base.html' %}
{% block content %}
<h1>{{blog.title}}</h1>
<div>
작성자: {{blog.writer}}
<br />
날짜: {{blog.pub_date}}
</div>
<hr />
<p>{{blog.body}}</p>
{% if blog.image %}
<p><img src="{{blog.image.url}}" alt="" /></p>
{% endif %}
<a href="{% url 'blog:update' blog.id %}">수정하기</a>
<a href="{% url 'blog:delete' blog.id %}">삭제하기</a>
<a href="{% url 'home' %}">돌아가기</a>
{% endblock %}