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

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

ただいまの
回答率

90.53%

  • jQuery

    6674questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • Python 3.x

    6314questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Ajax

    1089questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • Django

    1046questions

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

  • REST

    137questions

    REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

AjaxでinputタグのファイルをDjango REST FrameworkのAPIに渡したらMultiValueDictのkeyに入れられてしまう

解決済

回答 1

投稿

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

Python 3.6.5
Django 2.0.6

Django REST FrameworkのAPIに対してjQueryを利用したAjaxでinputタグに読み込んだ画像ファイルを送信したところ、
データがrequest.POSTのkeyにMultiValueDictとして入力されてしまってrequest.POST.get()で取得することが出来ません。
どうしたら通常の様なkeyに対するvalueとしてrequest.POSTへ入力される様になるのでしょうか。

データの送信処理自体はページからデータをAPIに対してjQueryを利用したAjaxでサーバへ送信して登録するという処理を既に作成していて、
Ajaxで送信されたデータが通常のrequest.POST.get()で取得可能な状態であることを確認してから流用しています。

実行時の出力
10
None
<QueryDict: {'[object Object]': ['']}>
<MultiValueDict: {'[object Object]': ['']}>
(省略)
AttributeError: 'MultiValueDict' object has no attribute 'iteritems'

AjaxにcontentType: 'multipart/form-data',を追加した場合、
10
None
<QueryDict: {}>
<MultiValueDict: {}>
(中略)
multipartparser.py", line 72, in init
raise MultiPartParserError('Invalid boundary in multipart: %s' % boundary.decode())
AttributeError: 'NoneType' object has no attribute 'decode'
というエラーが幾つも出力されます。

pcreate.js
var input_file_change = function () {
    input_file = document.getElementById('input_file');
    files = input_file.files;
    if (files[0] != 'undefined') {
        var fd = new FormData();
        var id = document.getElementById('photo_id').value;
        for (var i = 0; i < files.length; i++) {
            var file = files[i];

            fd.append('file', file);
        }

        $.ajax({
            url: 'http://localhost:8000/api/submit_picture/',
            type: 'POST',
            data: {
                'files': fd,
                'id': id,
            },
            processData: false,
            dataType: 'json',
//            contentType: 'multipart/form-data',
            success: function(response) {
                console.log('success');
            },
            error: function(e, x, r) {
                console.log(e);
            }
        });
    }
};
pcreate.html
<div class="col">
    <button class="btn btn-outline-primary" id="add_picture">画像の追加</button>
    <form method="POST" enctype="multipart/form-data" id="save_picture">
        <input type="file" name="files[]" style="display: none" id="input_file" multiple>
        <input type="hidden" value="{{ photo_id }}" id="photo_id">
        {% csrf_token %}
        <input type="submit" style="display: block" id="submit_picture" name="submit_picture">
    </form>
</div>
views.py
class PCreate(generic.TemplateView):
    template_name = 'photo_app/pcreate.html'

    def get_context_data(self, **kwargs):

        context = super(PCreate, self).get_context_data(**kwargs)
        context['photo_id'] = ''
        return context

class PictureSave(viewsets.ModelViewSet):

    queryset = Photo.objects.all()
    serializer_class = PictureSaveSerializer

    def create(self, request, *args, **kwargs):

        json_ary = []
        if request.method == 'POST':
            login_user_id = request.user.id
            photo_id = request.POST.get('id')
            files = request.POST.get('files')

            print(login_user_id)
            print(photo_id)
            print(request.POST)
            print(files)

            for filename, file in request.FILES.iteritems():
                print(file)
                dict = []

                photoObject = Photo(
                    title=request.FILES[filename].name,
                    image=file,
                    user_id = login_user_id,
                    photo_id=id
                )
                photoObject.save()

                dict.append({
                    'id':'%s' % photoObject.id,
                    'filename': request.FILES[filename].name,
                    'file': file,
                })

                json_ary.append(dict)

        return JsonResponse({"data":json_ary})
models.py
def get_upload_to(instance, filename):
    name = str(uuid.uuid4()).replace('-', '')
    extension = os.path.splitext(filename)[-1]
    return os.path.join(str(instance.photobook_id.id), name, extension)

class Photo(models.Model):

    use_in_migrations = True

    # ユーザーID
    user_id = models.PositiveIntegerField(_('ユーザーID'), default=0, blank=False)
    # 画像タイトル
    title = models.CharField(max_length=255,)
    # 画像データ
    image = models.ImageField(upload_to=get_upload_to, null=True, blank=True,)
    # 登録日時
    date_joined = models.DateTimeField(_('登録日時'), default=timezone.now)

    class Meta:
        app_label = 'photobook_app'
serializer.py
class PictureSaveSerializer(serializers.ModelSerializer):

    class Meta:
        model = Photo
        fields = '__all__'
urls.py
#写真の追加処理
router.register(r'submit_picture', PictureSave)

urlpatterns = [
    path('pcreate/', views.PCreate.as_view(), name='pcreate'),
]
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

check解決した方法

0

本質的な解決になりませんが、
時間の都合上からREST APIをDjango REST Frameworkで統一することを諦めて、defでrequest.POSTを受けた時にのみ処理する様にして実装することにしました。

pcreate.js

var files;

//inputタグ内のファイルが変更された場合
var input_file_change = function () {
    input_file = document.getElementById('input_file');
    files = input_file.files;
    if (files[0] != 'undefined') {
        var fd = new FormData($("#save_picture").get(0));

        $.ajax({
            url: 'http://localhost:8000/submit_picture/',
            type: 'POST',
            data: fd,
            processData: false,
            contentType: false,
            success: function(response) {
                console.log(response);
                console.log(response.data[0][0]);
            },
            error: function(e, x, r) {
               console.log(e);
            }
        });
    }
};
urls.py

# ページ設定
urlpatterns = [
    path('pcreate/', views.PCreate.as_view(), name='pcreate'),
    path('submit_picture/', views.PictureSave, name='upload_save'),
]
models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
import uuid
import os

def get_upload_to(instance, filename):
    name = str(uuid.uuid4()).replace('-', '')
    extension = os.path.splitext(filename)[-1]
    return os.path.join(str(instance.user_id), name + extension)

# 画像クラス
class Photo(models.Model):

    use_in_migrations = True

    # 画像タイトル
    title = models.CharField(max_length=255,)
    # 画像データ
    image = models.ImageField(upload_to=get_upload_to, null=True, blank=True,)
    # ユーザーID
    user_id = models.PositiveIntegerField(_('ユーザーID'), default=0, blank=False)
    # 登録日時
    date_joined = models.DateTimeField(_('登録日時'), default=timezone.now)

    class Meta:
        app_label = 'photo_app'
views.py

from django.http import JsonResponse
from django.core import serializers
from photobook_app.models.image import Photo
import base64
def PictureSave(request):
    if request.method == 'POST':
        try:
            login_user_id = request.user.id

            json_ary = []
            for file in request.FILES.getlist("files[]"):
                print(file)
                dict = []

                photoObject = Photo(
                    title=file.name,
                    image=file,
                    user_id = login_user_id,
                )
                photoObject.save()

                # 画像ファイルを読み込んでBASE64形式に変換
                image_path = photoObject.image.path
                b64 = base64.encodestring(open(image_path, 'rb').read()).decode('utf-8')

                dict.append({
                    'id':'%s' % photoObject.id,
                    'filename': photoObject.title,
                    'file': 'data:image/jpg;base64,%s' % b64,
                })
                json_ary.append(dict)

            response = HttpResponse(dict, content_type="application/json")
            return JsonResponse({"data":json_ary})
        except Exception as err:
            raise err

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

同じタグがついた質問を見る

  • jQuery

    6674questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • Python 3.x

    6314questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Ajax

    1089questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • Django

    1046questions

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

  • REST

    137questions

    REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。