본문 바로가기
Web Programming/django

[Django] 이미지 업로드

by 테리는당근을좋아해 2020. 6. 21.

목표

- 사용자가 이미지를 업로드할 수 있는 기능을 구현한다.

- 미디어 파일에 대해서 이해한다.

 

 

Media file

static 컨텐츠에는 두 가지 종류가 있다.

 

1) static file

- 개발자가 웹 애플리케이션 개발을 위해 개발 과정에서 저장한 파일(css, javascript, image)

 

2) media file

- 사용자가 웹 애플리케이션에 업로드한 파일(image)

 

저번 포스트에서 static file을 관리하는 방법을 알아보았으니,

 

이번에는 media file을 업로드하고 관리하는 방법을 알아보자

 

 

Media files 저장 및 전달 설정

media file을 저장하고 관리할 수 있는 디렉터리 설정과 url 전달 설정이 필요하다.

settings.py에 아래의 코드를 추가한다.

# 미디어 파일을 관리할 루트 media 디렉터리
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 각 media file에 대한 URL prefix
MEDIA_URL = '/media/'

 

urls.py에 아래의 코드를 추가한다.

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

urlpatterns = [
    ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

터미널에 아래 명령어 입력을 통해 pillow를 다운로드한다.

$ pip3 install pillow

 

pillow는 python3에서 이미지를 관리할 수 있는 패키지 이다.

 

 

Model 설정

이미지를 업로드하기 위한 Djagno 프로젝트 자체에 대한 설정은 끝났다.

이제 이미지를 업로드할 수 있도록 모델에 속성을 추가해주어야한다.

이미지는 테이블 속성 값으로 이미지 자체로 저장되어있는 것이 아닌 '/media/이미지이름.png' 처럼 path가 저장된다.

 

만약 게시글마다 이미지를 하나만 올릴 수 있다고 한다면 테이블을 아래처럼 구성될 것이고,

 

Model에서 클래스에 아래처럼 'ImageFiled' 하나만 추가해주면 된다.

class Post(models.Model):
    title = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    content = models.TextField()
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
    image = models.ImageField(upload_to='images/',blank=True, null=True)
    
    def __str__(self):
        return self.title

 

 

하지만, 게시글에 여러 개의 이미지를 저장하고 볼 수 있으려면 게시글과 이미지가 1:n 관계를 맺을 수 있도록 모델을 구성해주어야한다.

 

따라서 model에 Photo라는 새로운 클래스를 하나 만들고 Post와 1:n의 관계를 맺도록 ForeignKey 필드를 사용해 Post 테이블을 참조하도록 한다.

class Post(models.Model):
    title = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    content = models.TextField()
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
    
    def __str__(self):
        return self.title

class Photo(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True)
    image = models.ImageField(upload_to='images/', blank=True, null=True)

 

model에 대한 수정이 끝났다면 migration을 해준다.

 

 

admin 설정

admin 페이지에서 Photo 테이블을 볼 수 있도록 설정해보자.

 

이전의 Post와는 다른 설정을 해야한다.

 

Photo라는 테이블이 개별로 존재하는 것으로 나타내는 것이아닌,

Post 테이블의 각 레코드 안에 해당하는 Photo의 레코드를 나타내주어야하기 때문이다.

 

admin.py를 아래와 같이 수정해준다.

from django.contrib import admin
from .models import Post, Photo

# Photo 클래스를 inline으로 나타낸다.
class PhotoInline(admin.TabularInline):
    model = Photo

# Post 클래스는 해당하는 Photo 객체를 리스트로 관리하는 한다. 
class PostAdmin(admin.ModelAdmin):
    inlines = [PhotoInline, ]

# Register your models here.
admin.site.register(Post, PostAdmin)

 

 

admin 페이지

이제 admin 페이지에서 Post의 레코드 내에서 해당 Photo를 삽입/갱신/삭제가 가능하다.

 

! 일치하는 속성 값이 없다는 에러가 뜨는 경우

이미지 캡쳐본을 찍지 못했다. 

 

컬럼을 추가하면서 이전에 존재하던 레코드에 해당하는 컬럼 값이 존재하지 않는다는 에러가 뜨는 경우

migation할 때, 

 

$ python3 manange.py migrate --run-syncdb

 

를 통해 동기화 시켜주자.

 

 

Form

템플릿에서 이미지를 업로드할 수 있도록 form을 수정한다.

<form method="POST" action="{% url 'create' %}" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="text" name="title">
    <input type="text" name="content">
    <input type="file" name="imgs" multiple>
    <input type="submit" value="작성하기">
</form>

form 태그에 enctype="multipart/form-data" 속성을 추가하고,

이미지 파일을 여러개 입력 받을 수 있도록 '<input type="file" name="imgs" multiple>' 태그를 추가한다.

 

 

View

views.py를 아래처럼 수정한다.

from .models import Post, Photo

...

def create(request):
    if(request.method == 'POST'):
        post = Post()
        post.title = request.POST['title']
        post.content = request.POST['content']
        post.pub_date = timezone.datetime.now()
        post.user = request.user
        post.save()
        # name 속성이 imgs인 input 태그로부터 받은 파일들을 반복문을 통해 하나씩 가져온다 
        for img in request.FILES.getlist('imgs'):
            # Photo 객체를 하나 생성한다.
            photo = Photo()
            # 외래키로 현재 생성한 Post의 기본키를 참조한다.
            photo.post = post
            # imgs로부터 가져온 이미지 파일 하나를 저장한다.
            photo.image = img
            # 데이터베이스에 저장
            photo.save()
        return redirect('/detail/' + str(post.id))
    else:
        return render(request, 'new.html')

Post를 생성하도록 처리된 create 메소드 안에서 전달된 이미지를 반복문으로 하나씩 저장한다.

 

 

template

detail.html에 아래와 같은 코드를 작성해준다.

{% for photo in post.photo_set.all%}
    <img src="{{photo.image.url}}" width="50"><br>
{% endfor %}

저장할 때와 마찬가지로 반복문을 통해 해당 Post에 저장된 이미지를 하나씩 출력한다.

 

 

 

 

이미지가 잘 추가되는 것을 확인할 수 있다.

'Web Programming > django' 카테고리의 다른 글

[Django] 부트스트랩  (0) 2020.06.22
[Django] URL 관리  (0) 2020.06.22
[Django] 정적파일  (1) 2020.06.21
[Django] 모델 관계 (2)  (0) 2020.06.21
[Django] 모델 관계 (1)  (0) 2020.06.21

댓글