質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.86%

画像アップロード動作で、ファイルの保存・画像名の取得を行いたい。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 443

Lim-Nic

score 9

前提・実現したいこと

DjangoでSNSアプリを作成しており、画像アップロードの動作を組み込もうとしています。
基本的なSNS動作は書籍のサンプルコードを使用しており、そこから自分なりに機能を追加しています。

使用している書籍:Python Django超入門 著者:掌田 氏 (SNSサンプル:第5章~)

実現したい動作としては
① POST(投稿)ページから画像を添付
② POST!ボタンで送信した際、画像のURL情報(もしくは画像タイトル)を取得
③ Messageモデル(DB)に画像情報を保存
④ プロジェクトフォルダの/media/フォルダに画像を保存
⑤ SNSトップページに /media/画像タイトル で画像を出力
のような動作です。途中動作が前後する可能性もありますが、結果出力できれば良いと思っています。

発生している問題・エラーメッセージ

POSTページに表示している画像参照フォームは
image = forms.ImageField()
送信後の読み取りは
image = request.POST['image']
としています。

しかしこの状態では、入力された画像パスがそのまま取得されてしまい
DBの管理画面で確認すると、/media/D:/----- のような結果になってしまいます。
また、/media/フォルダに画像が保存できません。

ところが、DBにログインし管理画面から画像添付を行うと
/media/画像名 と自動的に画像名のみ出力され
/media/フォルダに画像が保存されます。

なんとかして、この2つの状況を足し合わせたいのですが・・・。

該当のソースコード

#settings.py(追記分)

#画像保存場所の指定
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
#urls.py(追記分)

#画面に画像を表示する為のもの
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
#models.py
from django.db import models
from django.contrib.auth.models import User

# Messageクラス
class Message(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE, \
            related_name='message_owner')
    group = models.ForeignKey('Group', on_delete=models.CASCADE)
    content = models.TextField(max_length=1000)
    share_id = models.IntegerField(default=-1)
    good_count = models.IntegerField(default=0)
    share_count = models.IntegerField(default=0)
    pub_date = models.DateTimeField(auto_now_add=True)
    image = models.ImageField(blank=True, null=True) #追加箇所

    #コメントとユーザー名を出力する
    def __str__(self):
        #__str__は、インスタンスを文字列に変換する
       return str(self.content) + ' (' + str(self.owner) + ')' 

    #シェアした元メッセージを出力する
    def get_share(self):
        return Message.objects.get(id=self.share_id)

    #日時の古い順に並べ替える
    class Meta:
        ordering = ('-pub_date',)
#forms.py

from django import forms
from.models import Message,Group,Friend,Good
from django.contrib.auth.models import User

# 投稿フォーム
class PostForm(forms.Form):
    #メッセージ入力欄(Textarea=広い入力ボックス)
    content = forms.CharField(max_length=500, \
            widget=forms.Textarea)

    image = forms.ImageField() #追加箇所

    def __init__(self, user, *args, **kwargs):
        super(PostForm, self).__init__(*args, **kwargs)
        #publicに自身のユーザー名を取得
        #自身のユーザーがオーナーのグループをリスト内包表記でタプル取得
        public = User.objects.filter(username='public').first()
        self.fields['groups'] = forms.ChoiceField(
            choices=[('-','-')] + [(item.title, item.title) \
                     for item in Group.objects. \
                     filter(owner__in=[user,public])],
        )
#views.py

from django.shortcuts import render
from django.shortcuts import redirect
from django.contrib.auth.models import User
from django.contrib import messages

from .models import Message,Friend,Group,Good
from .forms import GroupCheckForm,GroupSelectForm,\
        SearchForm,FriendsForm,CreateGroupForm,PostForm

from django.db.models import Q
from django.contrib.auth.decorators import login_required

#from django.urls import reverse_lazy
#from django.views import generic
#from .forms import UploadModelForm
#from .models import UploadFile

# メッセージのポスト処理 【Post!ボタン】
@login_required(login_url='/admin/login/')
def post(request):
    # POST送信の処理
    if request.method == 'POST':
        # 送信内容の取得(グループ名・コンテンツ(メッセージ))
        gr_name = request.POST['groups']
        content = request.POST['content']
        image = request.POST['image'] #追加箇所
        # Groupの取得(Noneの場合はpublicグループ)
        group = Group.objects.filter(owner=request.user) \
                .filter(title=gr_name).first()
        if group == None:
            (pub_user, group) = get_public()
        # Messageのインスタンスを作成し設定して保存
        msg = Message()
        msg.owner = request.user
        msg.group = group
        msg.content = content
        msg.image = image #追加箇所
        msg.save()
        # メッセージを設定
        messages.success(request, '新しいメッセージを投稿しました!')
        return redirect(to='/sns')


処理が重く、全て貼ることが出来ませんでした。
その他公開が必要なプログラム部分があれば教えてください。

試したこと

上記の状態です。
アップロードについては下記を参考にしています。
https://narito.ninja/blog/detail/92/
http://sr2460.hatenablog.com/entry/2019/02/14/213617?_ga=2.147647986.490112225.1550722323-1409412485.1430535027

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mistn

    2019/07/23 14:58

    ないならつけてください。つけていれば request.FILES['file'] で取得できると思います。

    一応公式ドキュメントおいておきます。
    https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/

    キャンセル

  • Lim-Nic

    2019/07/23 15:06

    image = request.FILES['image']
    でデータの受け取りを行い、
    msg.image = image
    でDBに保存したところ、理想通りの動作が確認出来ました。

    公式ドキュメントの情報もありがとうございます。
    念のためこの質問を確認される方のために。
    私はこちらのサイトを見ながらやっていました。(少し古いのかもしれませんが)
    https://djangoproject.jp/doc/ja/1.0/ref/request-response.html#django.http.HttpRequest.FILES

    よろしければ回答項目にコメントを頂ければ幸いです。

    キャンセル

  • mistn

    2019/07/23 15:22

    解決したようでなによりです。回答は自己回答しておいてください。

    ドキュメントの情報はDjangoのバージョンが1.0のものなので確かに古いですね。開発している環境が1.0なら問題ありませんが最新版で開発している場合は仕様が異なることもあるので気をつけてください。

    キャンセル

回答 1

check解決した方法

0

views.py
   image = request.POST['image']
            ↓↓変更
  image = request.FILES['image']

#request.POSTでは絶対パスでしか取得できず、
 fileを扱う際はrequest.FILESを使用することでファイル名(---.jpgなど)を取得できる。
post.html(投稿フォーム)
  <form action="{% url 'post' %}" method="post">
      ↓↓変更
  <form action="{% url 'post' %}" method="post" enctype="multipart/form-data">

#ファイルデータのやり取り(request.FILES)をする際は、
 ファイル送信フォーム内にenctype="multipart/form-data"を追記する必要がある。


上記2点を変更し、理想の動作が実現しました。

参考サイト:
■ 画像アップロード方法
https://narito.ninja/blog/detail/92/
⇒モデルを利用する
http://sr2460.hatenablog.com/entry/2019/02/14/213617?_ga=2.147647986.490112225.1550722323-1409412485.1430535027
⇒python、djangoで掲示板を作ってみたい③,④
■ 今回変更した部分の解説
https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/
https://djangoproject.jp/doc/ja/1.0/ref/request-response.html#django.http.HttpRequest.FILES
⇒古いですが、日本語版
また、python、djangoで掲示板を作ってみたい④にも同じ問題の記述がありました。(気づきませんでした)

※余談
現在の状態では、画像添付フォームが必須項目になっているのでrequiredを使って任意項目へ変更。
しかし、画像添付しないで送信したところimageが見つからないとエラーになったため
以下のように変更しました。おそらく無理矢理な状態なので、今後時間があれば見直します。

views.py
        try:
            image = request.FILES['image']
        except:
            pass
        else:
            msg.image = image
        finally:
            msg.save()

# try:でimageデータを取得し、エラーになればexcept:passでスルー。
 else:でエラーにならなければ、imageデータをDBに書き込み
 finally:でエラーの有無に関係なくDBを保存。

少しでも参考になれば幸いです。
アドバイス頂いた方、本当にありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる