파이썬 python/django

[django] ImageField Form 이미지 업로드하기.

Aytekin 2022. 6. 10. 15:14
728x90

django Form에서 이미지를 업로드해서 DB에 저장하는 기능을 구현하는 방법이다.

 

이미지 파일이 form에서 view로 넘어가지도 않는 문제, view에서 조회하는 문제들을 해결하였다.

1. media폴더 경로 설정

  • 프로젝트 폴더 settings.py 마지막줄에 MEDIA_URL, MEDIA_ROOT 추가
# setting.py

# ... 이외의 기본 세팅들

MEDIA_ROOT = BASE_DIR/'media'
MEDIA_URL = '/media/'
  • 프로젝트 폴더 urls.py에 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 추가
# urls.py

from django.contrib import admin
from django.urls import path, include
from user.views import index

# 이미지를 업로드하자
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
	... url conf ...
]

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

 

2. 모델 설계

제품 테이블을 만들고 거기에 이미지파일을 같이 저장하려고 모델을 만들었다.

ImageField를 사용하여 Image필드를 만들었다.

upload_to 옵션은 사용하지 않았기 때문에 프로젝트 폴더내의 media경로에 바로 이미지파일이 저장된다.

# models.py

class Product(models.Model):
    product = models.CharField(max_length=32,verbose_name='product_name')
    price = models.IntegerField(verbose_name='product_price')
    description = models.TextField(verbose_name='product_description')
    image = models.ImageField(blank=True, null=True, verbose_name='product_image')
    stock = models.IntegerField(verbose_name='product_stock')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='product_register_date')

    def __str__(self):
        return self.product

    class Meta:
        db_table = 'product_table'
        verbose_name = '제품'
        verbose_name_plural = '제품'
  • (선택) models.py에서 models.ImageField()에 upload_to옵션 추가. upload_to옵션은 위의 1번에서 지정된 media경로 내에서 다시 어느 경로로 저장할지 설정하는 옵션이다. 
  • "cannot use ImageField because Pillow is not installed."라는 오류메세지가 뜰 경우
    이미지처리를 위해서는 django의 추가 패키지가 필요한데 Pillow라는 라이브러리를 사용하는 가상환경에 설치해주면 된다.
    pip install Pillow 로 끝!

 

3. 폼(Form) 설계

forms.ImageField()를 이용하여 폼을 구성한다. 

# forms.py

class RegisterForm(forms.Form):
    image = forms.ImageField(
        error_messages={
            'required':'사진을 첨부해주세요'
        },
        label='image')

 

4. 뷰(View) 설계

함수형 뷰 또는 클래스 뷰 모두 똑같이 적용할 수 있다고 한다. 나는 클래스 뷰를 이용해서 만들어보았다.

폼 유효성검사 후 작업을 하기 위해서 form_valid함수를 오버라이딩했다.

form_valid함수는 form을 인자로 받는다.

form.cleaned_data로 폼에 입력된 값들을 가져올 수 있다.

form.cleaned_data는 딕셔너리 형태라서 key값을 조회해서 값을 가져올 수 있다.

 

난 처음에 form.data로 값을 조회했었는데 이때는 이미지파일을 불러오지 못해서 한동안 해맸었다.

data 속성에는 csrf토큰 값이 들어있는데 이상하게 이미지 파일은 저장이 되지 않았다. 이유는 잘 모르겠다..

여튼 form.data속성이 아닌 form.cleaned_data속성에서 폼에 입력한 값에 접근하여 사용하면 된다.

# views.py

class ProductRegister(FormView):
    template_name = 'product_register.html'
    form_class = RegisterForm
    success_url = '/product/list'

    def form_valid(self, form):
        product = Product(
            product = form.cleaned_data.get('product'),
            price = form.cleaned_data.get('price'),
            description = form.cleaned_data.get('description'),
            stock = form.cleaned_data.get('stock'),
            image = form.cleaned_data.get('image')
        )
        product.save()
        return super().form_valid(form)

 

5. html 템플릿 설계

form테그에 input테그를 넣어준뒤 submit으로 제출하면 된다.

이때 form 테그내에  enctype="multipart/form-data" 을 설정해주어야 한다.

그럼 입력한 이미지 값이 잘 넘어간다.

 

# product_register.html

<form method="POST" action="." enctype="multipart/form-data" style="user-select: auto;">
            {% csrf_token %}
            {% for field in form %}
            <div class="form-group">
                <lable for="{{ field.id_for_label }}">{{ field.label }}</lable>
                {% if field.name == 'description' %}
                <textarea class="form-control" name="{{ field.name }}" id="field.id_for_label" placeholder="상품설명을 써주세요~!"></textarea>
                {% else %}
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}" />
                {% endif %}
            </div>
            {% if field.errors %}
            <span style="color:red">{{ field.errors }}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">상품등록</button>

 

참고한 사이트

https://roseline124.github.io/django/2019/03/27/pickmeal-media.html

https://docs.djangoproject.com/en/3.0/topics/forms/

https://kgu0724.tistory.com/117

 

 

 

728x90