Python | Django | フォームにおいてModelを利用した画像のアップロード方法

2021年7月16日

公開日:2021/7/16

Pythonには,DjangoというWebアプリケーションフレームワークがある.フレームワークのため,Djangoを利用するとWebアプリを通常よりも短時間で開発することが可能になる.

前記事にて,「フォームにおける画像のアップロード方法」を記した.前記事での設定をそのまま引き継いだ上で,本記事では,「フォームにおいてモデルを利用した画像のアップロード方法」を以下5つの構成にて記す.

  1. 前記事のアップロード方法の復習
  2. 本記事の目的
  3. 画像アップロードの実装方法
  4. データ挿入とともに画像アップロード実装方法
  5. 日付を利用した画像保存フォルダの作成

◆実施環境

Python 3.8.8
Django 3.2.3

■フォームにおいてモデル(Model)を利用した画像のアップロード方法

  1. 前記事のアップロード方法の復習

前記事である「フォームにおける画像のアップロード方法」では,ブラウザで画像をアップロードすると以下画面になる.

上記の赤枠をクリックすると,ブラウザは以下画面になる.

ただし,VS Code内の"form_project/media"には以下のように画像が保存される.

  1. 本記事の目的

本記事では,ブラウザに画像が表示されるように実装する

  1. 画像アップロードの実装方法

“form_project/urls.py"に追記し,以下のように編集する.

from django.contrib import admin
from django.urls import path, include
from django.conf import settings # 追記箇所
from django.conf.urls.static import static # 追記箇所

urlpatterns = [
path('admin/', admin.site.urls),
path('form_application/', include('form_app.urls')),
]

if settings.DEBUG: # 以下追記箇所
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行する(移行方法の詳細はこちら)."cd form_project"を実行することによって,”form_project”のディレクトリに移動する.その後,”python manage.py runserver”を実行すると,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py runserver

System check identified no issues (0 silenced). # 以下出力箇所
July 16, 2021 - 03:47:41
Django version 3.2.3, using settings 'form_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

“http://127.0.0.1:8000/"をクリックするとブラウザによって以下ページに遷移する.

“http://127.0.0.1:8000/form_application/upload_pic"を入力すると以下画面に遷移する.

“ファイルを選択"をクリックし,画像を選択後,"保存"をクリックすると以下画面になる.

上記の赤枠のURLをクリックすると以下のようにアップロードした画像をブラウザで確認することができる.

  1. データ挿入とともに画像アップロード実装方法

“form_app/models.py"を追記し,以下のように編集する.

from django.db import models

class Bird(models.Model):
  birdname = models.CharField(max_length=50)
  title = models.CharField(max_length=100)
  review = models.CharField(max_length=300)
  author = models.CharField(max_length=50)

class ModelSetName(models.Model):
  name = models.CharField(max_length=50)
  birthplace = models.CharField(max_length=50)

class Sweet(models.Model): # 以下追記箇所
  name = models.CharField(max_length=50)
  price = models.IntegerField()
  picture = models.FileField(upload_to='picture/')

ターミナルを開き,仮想環境に移行し,”form_project”のディレクトリに移動する.その後,”python manage.py makemigrations form_app”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py makemigrations form_app

Migrations for 'form_app': # 以下出力箇所
  form_app\migrations\0003_sweet.py
    - Create model Sweet

その後,"python manage.py migrate"を実行する.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py migrate

Operations to perform: # 以下出力箇所
  Apply all migrations: admin, auth, contenttypes, form_app, sessions
Running migrations:
  Applying form_app.0003_sweet... OK

“form_app/migrations"には"0003_sweet.py"が作成される.

SQLiteをインストールし(詳細はこちら参照),"SQLITE EXPLORER"を開くと,Tableの"form_app_sweet"を以下赤枠にて確認できる.

“form_app/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from django.forms import formset_factory, modelformset_factory
from . import forms
from . models import ModelSetName
import os
from django.core.files.storage import FileSystemStorage

def index(request):
  return render(request, 'form_temp/index.html')

def form_page(request):
  form = forms.ChocolateInfo()
  if request.method == 'POST':
    form = forms.ChocolateInfo(request.POST)
    if form.is_valid():
      print('submission is successful')
      print(form.cleaned_data)
    else:
      print('submission is failed')

  return render(
    request,'form_temp/form_page.html', context={
      'form':form
    }
  )

def form_bird(request):
  form = forms.BirdModelForm()
  if request.method == 'POST':
    form = forms.BirdModelForm(request.POST)
    if form.is_valid():
      form.save()
  return render(
    request, 'form_temp/form_bird.html', context={'form':form}
  )

def form_set_name(request):
  TestFormset = formset_factory(forms.FormSetName, extra=4)
  formset = TestFormset(request.POST or None)
  if formset.is_valid():
    for form in formset:
      print(form.cleaned_data)
  return render(
    request, 'form_temp/form_set_name.html',
    context = {'formset':formset}
  )

def modelform_set_name(request):
  TestModelFormSet = modelformset_factory(ModelSetName, form=forms.ModelFormSetName, extra=4)
  modelformset = TestModelFormSet(request.POST or None)
  if modelformset.is_valid():
    modelformset.save()
  return render(
    request, 'form_temp/modelform_set_name.html', 
    context={'modelformset':modelformset}
  )

def upload_pic(request):
  if request.method == 'POST' and request.FILES['upload_file']:
    upload_file = request.FILES['upload_file']
    file_store = FileSystemStorage()
    file_path = os.path.join('upload', upload_file.name)
    file = file_store.save(file_path, upload_file)
    uploaded_file_url = file_store.url(file)
    return render(request, 'form_temp/upload_file.html', context={
      'uploaded_file_url': uploaded_file_url
    })
  return render(request, 'form_temp/upload_file.html')


def upload_model_pic(request): # 以下追記箇所
  sweet = None
  if request.method == 'POST':
    form = forms.SweetForm(request.POST, request.FILES)
    if form.is_valid():
      sweet = form.save()
  else:
    form = forms.SweetForm()
  return render(request, 'form_temp/upload_model_pic.html', context={
    'form': form,
    'sweet': sweet
  })

“form_app/urls.py"に追記し,以下のように編集する.

from django. urls import path
from . import views

app_name = 'form_application'

urlpatterns = [
  path('', views.index, name='index'),
  path('form_page/', views.form_page, name='form_page'),
  path('form_bird/', views.form_bird, name='form_bird'),
  path('form_set_name/', views.form_set_name, name='form_set_name'),
  path('modelform_set_name/', views.modelform_set_name, name='modelform_set_name'),
  path('upload_pic/', views.upload_pic, name='upload_pic'),
  path('upload_model_pic/', views.upload_model_pic, name='upload_model_pic'), # 追記箇所
]

“form_app/forms.py"に追記し,以下のように編集する.

from django import forms
from django.core import validators

from .models import Bird, ModelSetName, Sweet # 追記箇所

class ChocolateInfo(forms.Form):
  chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
  chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
  mail = forms.EmailField(label='メールアドレス')
  confirmed_mail = forms.EmailField(label='メールアドレス再入力')
  is_available_for_sale = forms.BooleanField(label='販売中')
  birthday = forms.DateField(required=False, label='開発日')
  price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
  
  flavor = forms.ChoiceField(choices=(
     (1,'Original Chocolat'),
     (2,'Double Chocolat'),
     (3,'Original White'),
     (4,'Rich Matcha'),
     ), widget=forms.RadioSelect, label='味')
  
  maker_factory = forms.MultipleChoiceField(choices=(
     (1,'Ibaraki Moriya-city'),
     (2,'Saitama Sakado-city'),
     (3,'Aichi Inazawa-city'),
     (4,'Osaka Takatsuki-city'),
     ), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
  
  homepage = forms.URLField(
    label='ホームページ',
    widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'}))

  def __init__(self,*args,**kwargs):
    super(ChocolateInfo, self).__init__(*args,**kwargs)
    self.fields['flavor'].widget.attrs['id']='id_flavor'
    self.fields['maker_factory'].widget.attrs['class']='maker_factory_class'

  def clean_chocolatename(self):
     chocolate_name = self.cleaned_data['chocolate_name']
     if not chocolate_name.startswith('F'):
        raise forms.ValidationError('名前は「F」から始めてください')

  def clean(self):
     cleaned_data = super().clean()
     mail = cleaned_data['mail']
     confirmed_mail = cleaned_data['confirmed_mail']
     if mail != confirmed_mail:
       raise forms.ValidationError('メールアドレスが一致しません')

class BaseForm(forms.ModelForm):
  def save(self,*args,**kwargs):
    print(f'{self.__class__.__name__} is successful')
    return super(BaseForm, self).save(*args,**kwargs)

class BirdModelForm(BaseForm):
  birdname = forms.CharField(label='Birdname')
  title = forms.CharField(label='Title')
  review = forms.CharField(
    label='review', widget=forms.Textarea(attrs={
      'rows':15,
      'cols':25,}))
  
  author = forms.CharField(label='Author')

  class Meta:
    model = Bird
    fields ='__all__'

  def save(self,*args,**kwargs):
    ipt = super(BirdModelForm, self).save(commit=False,*args,**kwargs)
    ipt.birdname = ipt.birdname.lower()
    print(type(ipt))
    print('save is successful')
    ipt.save()
    return ipt

  def clean_birdname(self):
    birdname = self.cleaned_data.get('birdname')
    if birdname == 'this is error':
      raise validators.ValidationError('そのbirdnameはエラーです')
    return birdname

  def clean_title(self):
    title = self.cleaned_data.get('title')
    if title == 'this is error':
      raise validators.ValidationError('そのtitleはエラーです')
    return title

  def clean_review(self):
    review = self.cleaned_data.get('review')
    if review == 'this is error':
      raise validators.ValidationError('そのreviewはエラーです')
    return review

  def clean_author(self):
    author = self.cleaned_data.get('author')
    if author == 'this is error':
      raise validators.ValidationError('そのauthorはエラーです')
    return author
  
  def clean(self):
    cleaned_data = super().clean()
    author = cleaned_data.get('author')
    checking_exists = Bird.objects.filter(author=author).first()
    if checking_exists:
      raise validators.ValidationError('そのauthorはすでに存在しています')
    
class ModelFormSetName(forms.ModelForm):
  name = forms.CharField(label='名前')
  birthplace = forms.CharField(label='出身地')

  class Meta:
    model = ModelSetName
    fields = '__all__'

class SweetForm(forms.ModelForm): # 以下追記箇所
  class Meta:
    model = Sweet
    fields = '__all__'

“form_project/templates/form_temp"に"upload_model_pic.html"を作成する.

“upload_model_pic.html"は以下のように編集する.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form Upload</title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="保存">
</form>
{% if sweet %}
<p>sweet name: {{ sweet.name }}</p>
<p>price: {{ sweet.price }}</p>
<img src="{{ sweet.picture.url }}">
{% endif %}
</body>
</html>

ターミナルを開き,仮想環境に移行する."cd form_project"を実行することによって,”form_project”のディレクトリに移動する.その後,”python manage.py runserver”を実行すると,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py runserver

System check identified no issues (0 silenced). # 以下出力箇所
July 16, 2021 - 19:14:05
Django version 3.2.3, using settings 'form_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

“http://127.0.0.1:8000/"をクリックするとブラウザによって以下ページに遷移する.

“http://127.0.0.1:8000/form_application/upload_model_pic/"を入力すると以下画面に遷移する.

内容を記入し,"ファイルを選択"によって画像を選択し,"保存"をクリックする.

以下のようにブラウザに出力される.

VS Codeの"form_project/media"にアップロードした"montblanc.jpg"が保存されている.

Tableの"form_app_sweet"を右クリックをし,"Show Table"をクリックすると以下データが保存されているのが分かる.

  1. 日付を利用した画像保存フォルダの作成

“form_app/models.py"を追記し,以下のように編集する.

from django.db import models

class Bird(models.Model):
  birdname = models.CharField(max_length=50)
  title = models.CharField(max_length=100)
  review = models.CharField(max_length=300)
  author = models.CharField(max_length=50)

class ModelSetName(models.Model):
  name = models.CharField(max_length=50)
  birthplace = models.CharField(max_length=50)

class Sweet(models.Model):
  name = models.CharField(max_length=50)
  price = models.IntegerField()
  picture = models.FileField(upload_to='picture/%Y/%m') # 変更箇所

上記を保存し,"http://127.0.0.1:8000/form_application/upload_model_pic/"に移動し,内容を記入し,"ファイルを選択"によって画像を選択し,"保存"をクリックする.

以下のようにブラウザに出力される.

VS Codeの"form_project/media"に日付入りのフォルダが作成され,アップロードした"montblanc.jpg"が保存されている.

Tableの"form_app_sweet"には,データが以下のように保存される.

以上