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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

11365閲覧

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

Lim-Nic

総合スコア18

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/07/22 08:31

編集2019/07/23 04:10

前提・実現したいこと

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/ツールのバージョンなど)

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mistn

2019/07/23 01:47

views.pyのコードが全く読めないので修正してもらえませんか?
Lim-Nic

2019/07/23 04:08

修正しました。失礼しました。
mistn

2019/07/23 04:53 編集

確認したいのですが、request.POST['image']は画像のデータですか? ファイルは request.FILES で受け取ると思うのですが。
Lim-Nic

2019/07/23 05:09 編集

画像のデータをとっているつもりで書いていました。 file形式で読み取らないといけないと思うのですが、やっぱり文字列でしか取得できない形ですよね。 ※追記 私の考え方が違うのかもしれませんが、 POSTで送信した内容(メッセージやグループ、イメージ)の中からimageを選択している。 というのが request.POST['image'] ではないのでしょうか?
mistn

2019/07/23 05:26

私自身で確かめてはいないので間違っているかもしれませんが、今回の場合 request.POST['image'] はアップロードされた画像の絶対パスなんだと思います。一度 print(request.POST['image']) をやってコンソールに表示させてみて確認してください。
Lim-Nic

2019/07/23 05:33

D:\thMO83JL8Z01.jpg と絶対パスで帰ってきています。 また、request.FILES['image']とすると django.utils.datastructures.MultiValueDictKeyError: 'image' とエラーが表示されます。 POSTの内容は csrfmiddlewaretoken 'ToIQ3pOwuJJk9wLmMeKIbPXl3AoTchvTbGKycBGK5pFa3xiGdUI8r5L94G0JTu0C' content 'a' image 'D:\\thMO83JL8Z01.jpg' groups '-' FILESの内容は No FILES data です。
mistn

2019/07/23 05:41

htmlのformタグの属性に enctype="multipart/form-data" はついてますか?
Lim-Nic

2019/07/23 05:50 編集

付いていません。 <input name="image" id="id_image" required="" type="file" accept="image/*"> フォームは image = forms.ImageField() 以外特に追加設定はしていないと思います。 ※追記 失礼しました。formタグはこっちですよね。 <form action="/sns/post" method="post">
Lim-Nic

2019/07/23 05:56

連投すみません。 enctype="multipart/form-data"を追記して実行したところ、 thMO83JL8Z01.jpg とファイル名のみ出力されました。ありがとうございます。 print()から実際の処理に変更して実行してみます。
Lim-Nic

2019/07/23 06: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 06:22

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

回答1

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を保存。

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

投稿2019/07/23 07:19

Lim-Nic

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問