CSVファイルアップロード時のエラー処理について

288 views
Skip to first unread message

sinf...@gmail.com

unread,
Jan 27, 2018, 9:08:02 AM1/27/18
to django-ja
初めての投稿になります。

当方、Django初心者のため基本的な質問でしたら申し訳ないのですが、以下のエラー処理をどのように実装したらよいか
調べてもいまいちうまく実現できないため、ご教授よろしくお願いいたします。

【実現したいこと】
以下の処理で、CSVファイルを指定してデータ登録する処理(①~③)は問題なく実装できました。

①HTMLページ上から「ファイルを選択」ボタンを押して、CSVファイル選択する 
②「データ登録」ボタンを押す 
③CSVのデータを取り込んでデータベースにデータを登録。


ただし、①でファイルを選択しない状態で、②の「データ登録」ボタンを押すと、以下のようなエラーが
発生するため、ファイルを選択しない状態で「データ登録」ボタンを押した場合は、「ファイルを選択してください」
といったエラーポップアップメッセージを表示したい。(難しいなら単純なエラー文字列を表示するだけでもいい)

--------------------------------------------------------------------------------------------------------------

Request Method:POST
Request URL:http://127.0.0.1:8000/server/csv_import/
Django Version:2.0.1
Exception Type:MultiValueDictKeyError
Exception Value:
'csv'
--------------------------------------------------------------------------------------------------------------

以下、①~③の処理に関するコードの概要です。
=========================================
HTMLのCVSファイル選択&データ登録のソース部分
=========================================
※urlで指定している「csv import」はviews.py内で定義したCSVデータ登録用の関数名
-----------------------------------------------------------------------------------------------------------------------------
<form action='{% url "csv_import" %}' method='POST' enctype="multipart/form-data">
</div>

<div class="alert alert-info alert-dismissible">
    <h3>一括データ登録</h3>
  <div class="btn-group">
    <input type='file' name='csv'>
  </div>

  <div class="btn-group">
    <input type="submit" class="btn btn-danger" value="データ登録">
  </div>
    {% csrf_token %}
  </form>
</div>
-----------------------------------------------------------------------------------------------------------------------------

=========================================
csv import関数のソース

ServerInfoはモデル名です。(environment_id ,server_name, os_version,cpu,memoryの4つの要素)
=========================================
def csv_import(request):
        
    form_data = TextIOWrapper(
       request.FILES['csv'].file, encoding='sjis')
    if form_data:
        csv_file = csv.reader(form_data)
        for line in csv_file:
             serverinfo, created = ServerInfo.objects.get_or_create(
                 environment_id = line[0],
                 server_name = line[1],
                 os_version = line[2],
                 cpu = line[3],
                 memory = line[4],
                 )
             serverinfo.save()
    return redirect('index')

------------------------------------------------------------------------------------

おそらく、views.py内のcsv_import関数内でtry exceptでエラー処理をするかなにかをすればよいのかなというあたりは
ついているのですが、いろいろやってもうまくいかず悩んでいる状況です。

以上、よろしくお願いいたします。

MURAOKA Yusuke

unread,
Jan 27, 2018, 9:33:49 AM1/27/18
to djan...@googlegroups.com
村岡です。

Djangoで作られてるサーバサイドで処理するなら、ポップアップは一筋縄ではいきませんが、request.FILESがおそらくそのエラーの対象のMultiValueDictなので、
request.FILES['csv']で取り出す処理の直前までに

if 'csv' in request.FILES:
    ファイルがなかった時のレスポンスを返す

こんな感じの処理を差し込めばよいかと。

2018年1月27日(土) 23:08 <sinf...@gmail.com>:
--
--
----------------- http://djangoproject.jp/ -----------------
You received this message because you are subscribed to the Google Groups "django-ja" group.
To post to this group, send email to djan...@googlegroups.com
To unsubscribe from this group, send email to django-ja-...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/django-ja
---
このメールは Google グループのグループ「django-ja」に登録しているユーザーに送られています。
このグループから退会し、グループからのメールの配信を停止するには django-ja+...@googlegroups.com にメールを送信してください。
その他のオプションについては https://groups.google.com/d/optout にアクセスしてください。

sinf...@gmail.com

unread,
Jan 27, 2018, 8:10:44 PM1/27/18
to django-ja
村岡さん

お世話になります。
原です。

早々にご回答ありがとうございます!
まだ基本的な部分が理解しきれていなかったのですが、頂いた回答から糸口が見つかりました。
request.FILESは辞書型でfilenameキーを持っているので、そこにファイル名[csv]がなければファイルが指定されていないと判断すればいいということなんですね。

以下のような感じでファイル未選択時だった場合にエラー画面に遷移するという
ロジックを実装できました。

①views.pyのcsv_import関数に以下の赤字を追加。
-----------------------------------------------------------------------------------------------------
def csv_import(request):
    if 'csv' not in request.FILES:
    #if csv file not selected, move to error page 
        return redirect(file_not_found)
    
    form_data = TextIOWrapper(request.FILES['csv'].file, encoding='sjis')
    if form_data:
        csv_file = csv.reader(form_data)
   ・・・・省略・・・・
-----------------------------------------------------------------------------------------------------

②urls.pyの編集
以下のようなurlを追加
    url(r'^csv_import/reg_error/$', views.file_not_found, name='file_not_found'),


③views.pyにファイル未選択時にエラー画面(file_not_found.html)に遷移する関数を追加


上記がスマートな実装方法なのかはわかりませんがやりたいことは実装できました。
より望ましい設計方法などあればご教授いただければ幸いです。



2018年1月27日土曜日 23時33分49秒 UTC+9 MURAOKA Yusuke:

Kosei Kitahara

unread,
Jan 28, 2018, 3:05:07 AM1/28/18
to djan...@googlegroups.com
北原です

まず大前提として、
ユーザーから送信されたデータをバリデーションなしに操作するのは非常に危険です。
フォームのデータは適切なバリデーションを実施後取り扱うのを推奨します。
ファイルアップロード、及びバリデーションの利用例については以下を参考にしてください。


FileField で required 等を指定してバリデーションを実行することで、後続の処理が安全になります。

また、フォームバリデーション後のフィールドエラー表示のメカニズムなどは以下が参考になります。


Kosei Kitahara... 〆(..

To unsubscribe from this group, send email to django-ja-unsubscribe@googlegroups.com

For more options, visit this group at http://groups.google.com/group/django-ja
---
このメールは Google グループのグループ「django-ja」に登録しているユーザーに送られています。
このグループから退会し、グループからのメールの配信を停止するには django-ja+unsubscribe@googlegroups.com にメールを送信してください。
その他のオプションについては https://groups.google.com/d/optout にアクセスしてください。

MURAOKA Yusuke

unread,
Jan 28, 2018, 3:06:32 AM1/28/18
to djan...@googlegroups.com
村岡です。

動いたのですね、よかったです。

ちょっと発展的にはなりますが、Djangoにはそのifブロックをところをビューのロジックから分離する
フォーム(Form)という概念と機能実装があります。もし興味あればごらんください。

あとDjangoからは離れてしまうので省こうかどうか迷ったのですが、
UXの観点からだとフォームの要素が未入力だったときは、サーバにリクエストを送るまえに
ユーザにレスポンスが表示されると望ましいです。
原さんがおっしゃっていたポップアップでエラー表示するってやつです。
それはサーバサイドのDjangoでやるのではなく、ブラウザのなかで動くJavaScriptや
それらの発展的な技術群(TypeScript, React, .....)で作るのが一般的です。
こちらももし興味あれば。

2018年1月28日(日) 10:10 <sinf...@gmail.com>:
To unsubscribe from this group, send email to django-ja-unsubscribe@googlegroups.com

For more options, visit this group at http://groups.google.com/group/django-ja
---
このメールは Google グループのグループ「django-ja」に登録しているユーザーに送られています。
このグループから退会し、グループからのメールの配信を停止するには django-ja+unsubscribe@googlegroups.com にメールを送信してください。
その他のオプションについては https://groups.google.com/d/optout にアクセスしてください。

sinf...@gmail.com

unread,
Jan 28, 2018, 4:08:52 AM1/28/18
to django-ja
北原さん

初めまして。原です。

情報ありがとうございます。
ご指摘いただけると大変助かります。
初心者だからとりあえず動けばいいかなという感じで勉強を進めていましたが、最初から適切なバリデーションを実施後にデータを取り扱うように実装することが重要(というか基本)ですね・・

今回私が追加したバリデーションは「単にファイルが選択されていない」場合だけを考慮したものなので適切なバリデーションではない・・ということだと理解しました。

頂いた情報をもとに適切なバリデーションの方法等について学習したいと思います。
学習を進めていく中でどうしてもわからない部分がありましたら、また質問させていただくことがあるかと思いますが、その際はよろしくお願いいたします。


2018年1月28日日曜日 17時05分07秒 UTC+9 Kosei Kitahara:

sinf...@gmail.com

unread,
Jan 28, 2018, 4:24:03 AM1/28/18
to django-ja
村岡さん

原です。

情報ありがとうございます。

ちょうどこれからフォーム(Form)の学習にとりこもうかというまだまだ超初心者なので、ご指摘大変参考になります。

以下の点、いろいろ調べて勉強してみます。

「ifブロックをところをビューのロジックから分離」→Formで実装
「UXの観点でリクエストを送る前にレスポンスを表示がよい」→ポップアップメッセージを表示
   ※Djangoだけでは実装できない→JavaScriptやTypeScript, React等の技術と組み合わせる。

また質問させていただく機会があるかと思いますが、よろしくお願いいたします。



2018年1月28日日曜日 17時06分32秒 UTC+9 MURAOKA Yusuke:
Reply all
Reply to author
Forward
0 new messages