본문 바로가기

Python/Django

이미지 업로드: Django ImageField # Ajax

728x90
반응형

Django

 

파일이 저장될 디렉토리와, 그 디렉토리를 가리킬 URL 설정

 

settings.py

import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
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 설치

pip install pillow

pillow를 설치 하지 않으면 아래 모델 생성에서 ImageField를 사용하지 못하는 에러가 발생

 

model 생성 

from django.db import models

class Home(models.Model):
    title = models.CharField(max_length=30)
    banner = models.ImageField(null=True)
    # banner = models.ImageField(upload_to='images', null=True)

upload_to 옵션을 넣어서, MEDIA_ROOT 안에 추가적인 디렉토리에 저장할 수 있습니다.

 

migrate

python manage.py makemigrations && python manage.py migrate

 

Frontend에서 파일 보내기

1) HTML만 이용

<body>
  <h1>Home</h1>
    <form method="POST" enctype="multipart/form-data" action="/">
      {% csrf_token %}
      <input type="file" name="banner">
      <input type="text" name="title">
      <button>submit</button>
    </form>
</body>

 

2) Ajax 이용

static 디렉토리 설정, csrftoken 설정은 생략하고 설명하겠습니다.

{% load static %}
<head>
  <script src="{% static 'js/image.js' %}"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
  <h1>Home</h1>
    <input type="file" name="banner">
    <input type="text" name="title">
    <button onclick="sendData()">submit</button>
</body>

 

javascript file을 사용할 것이므로 {% load static %}

사용할 javascript file 호출 (static/js/image.js)

그 아래에는 ajax를 사용하기 위해 jquery 호출

ajax를 통해서 보낼것이므로 form은 지우고 진행해 보겠습니다.

  (form태그가 없어야 하는게 아니라 없이도 가능한걸 보기 위해서 입니다.)

 

static/js/image.js

 

function sendData(){
    let formData = new FormData();
    let banner = document.querySelector('[name="banner"]').files[0];
    let title = document.querySelector('[name="title"]').value;
    formData.append("banner", banner);
    formData.append('title', title);

    $.ajax({
        url: '/',
        method: 'POST',
        processData: false,
        contentType: false,
        enctype: 'multipart/form-data',
        data: formData,
        success: function (data) {
            window.location.reload();
        },
    });
}

파일에 대한 접근:

  type='file'인 input에 파일이 담기면, 하나만 담았어도 files라는 Array에 담기기 때문에 files[0] 으로 접근합니다.

FormData에 data 담기:

  javascript로 이미지 파일을 보내기 위해서는 FormData()를 이용해서 이 안에 파일을 담아서 보내야 합니다.

 

ajax 에서는 아래와 같은 설정을 해야하는 것이 일반적으로 ajax를 보낼때와 다른 차이점입니다.

processData: false,
contentType: false,
enctype: 'multipart/form-data',

 

Backend에서 파일 받기

from django.shortcuts import render
from home.models import Home


def home(request):
    banner_image = request.FILES.get('banner')
    Home.objects.create(banner=banner_image)
    return render(request, 'home.html')

 

* 이미지 파일 이름을 변경하면서 저장

 backend에서 이미지 파일을  변경

from django.shortcuts import render
from home.models import Home


def home(request):
    banner_image = request.FILES.get('banner')
    ext = str(banner_image).split(".")[-1]
    image_name = f'new_name.{ext}'
    home = Home.objects.create(title='test')
    home.banner = banner_image
    home.banner.name = image_name
    home.save()
    return render(request, 'home.html')

 

* 주의사항

 아래와 같이 ImageField도 함께 생성하면서 바로 name을 변경하는 것은 적용이 안됩니다.

 위와 같이 ORM Query로 ImageField 없이 row를 생성하고, 그 이후에 ImageField에 파일을 넣고 이름을 변경해야 합니다.

from django.shortcuts import render
from home.models import Home


def home(request):
    banner_image = request.FILES.get('banner')
    ext = str(banner_image).split(".")[-1]
    image_name = f'new_name.{ext}'
    home = Home.objects.create(title='test', banner=banner_image)
    home.banner.name = image_name
    home.save()
    return render(request, 'home.html')

 

* 이미지가 업로드될 디렉토리를 커스텀하기

from django.db import models
from datetime import datetime

def custom_upload_to():
    return datetime.now()

class Home(models.Model):
    title = models.CharField(max_length=30)
    banner = models.ImageField(upload_to=custom_upload_to(), null=True)

위와 같은 식으로 upload_to를 정해두면, 해당하는 조건대로(위에서는 row가 생성되는 시기)  MEDIA_ROOT로 설정한 기본 디렉토리 안에서 추가로 디렉토리가 정해집니다. 

 

위와 같이 upload_to를 설정해서 저장할 경우, 파일이름을 변경하면서 동시에 create 가능!!!!!!!

728x90
반응형