LoginSignup
kmnrk3131
@kmnrk3131 (ひろすけ)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Djangoでエラー文をhtmlに表示させたい

解決したいこと

Djangoでエラー文をhtmlに表示させたい

Djangoでブラウザ上でcsv処理をするWebアプリをつくっています。
formで入力したデータに不備があった際にエラーが発生します。
エラー画面を表示させずにバリデーションのような警告文をhtmlに表示するようにしたいです。
解決方法を教えて下さい。

forms.py

from django import forms
from django.core.validators import FileExtensionValidator
from django.core.exceptions import ValidationError

class UploadForm(forms.Form):
    testfile = forms.FileField(
        validators=[FileExtensionValidator(['csv'])]
    )
    columuns = forms.CharField(max_length=255)
    code = forms.CharField(max_length=2000)

views.py

def Tool_extractView(request):
    if request.method == 'POST':

        upload = UploadForm(request.POST, request.FILES)

        if upload.is_valid():
            form_data = upload.cleaned_data
            file = form_data["testfile"]
            code = form_data["code"]
            columuns = form_data["columuns"]

            #文字コード識別
            read_file = file.read()
            result = chardet.detect(read_file)
            enc = result['encoding']

            if enc == None:
                enc = 'cp932'

            #読み込み、編集
            file_data = pd.read_csv(io.StringIO(read_file.decode(enc)), delimiter=',')
            df = tool_extract_process(file_data,columuns,code)
            response = to_csv(df,enc)
            return response

        else:
            return render(request, "tool_extract.html", {'form':upload})

    else:
        upload = UploadForm()
        return render(request, "tool_extract.html", {'form':upload})

functions.py

#csv書き出し関数
def to_csv(df,enc):
    type_data = 'text/csv; charset=' + enc
    response = HttpResponse(content_type=type_data)
    response['Content-Disposition'] = 'attachment; filename="result.csv"'

    df.to_csv(path_or_buf = response, encoding = enc, index=False)

    return response


#csv行抽出関数
def tool_extract_process(file,columuns,code):
    df_result = file.loc[file[columuns].str.contains(code)]#ここでエラーが発生するのでhtmlにエラー文を渡して処理を中断したい。
    return df_result

tool_extract.html

<div class="form-group">
    {% if form.errors %}
    <ul class="alert alert-danger list-unstyled">
    {% for error in form.testfile.errors %}
    <li>{{ error }}</li>
    {% endfor %}
    </ul>
    {% endif %}


    <form method="POST" class="form" enctype="multipart/form-data">
        {% csrf_token %}
        <div class="file-upload-wrapper">
            <p>ファイルを選択<br>
                {{ form.testfile }}
            </p>
            <p>基準とする列名を入力<br>
                {{ form.columuns }}
            </p>
            <p>キーワードを入力<br>
                {{ form.code }}
            </p>
        </div>
        <p>ファイルをアップロードし、処理したファイルをダウンロードする</p>
        <button type="submit" class="save btn btn-outline-primary">実行</button>
    </form>
</div>

自分で試したこと

try exceptで例外処理

functions.py

#csv行抽出関数
def tool_extract_process(file,columuns,code):
    try:#ここを変更
        df_result = file.loc[file[columuns].str.contains(code)]#ここを変更
    except KeyError:#ここを変更
        error_messse = '存在していない文字が入力されています。'#ここを変更

    return df_result, error_messse

views.py

def Tool_extractView(request):
    if request.method == 'POST':

        upload = UploadForm(request.POST, request.FILES)

        if upload.is_valid():
            form_data = upload.cleaned_data
            file = form_data["testfile"]
            code = form_data["code"]
            columuns = form_data["columuns"]

            #文字コード識別
            read_file = file.read()
            result = chardet.detect(read_file)
            enc = result['encoding']

            if enc == None:
                enc = 'cp932'

            #読み込み、編集
            file_data = pd.read_csv(io.StringIO(read_file.decode(enc)), delimiter=',')
            after_file = tool_extract_process(file_data,columuns,code)#ここを変更
            df, error = after_file[0], after_file[1]#ここを変更
            response = to_csv(df,enc)
            return response, error#ここを変更

        else:
            return render(request, "tool_extract.html", {'form':upload})

    else:
        upload = UploadForm()
        return render(request, "tool_extract.html", {'form':upload})

tool_extract.html

<div class="form-group">
    {% if form.errors %}
    <ul class="alert alert-danger list-unstyled">
    {% for error in form.testfile.errors %}
    <li>{{ error }}</li>
    {% endfor %}
    </ul>
    {% endif %}

    {% if error %}<!--ここを変更-->
    <ul class="alert alert-danger list-unstyled"><!--ここを変更-->
    <li>{{ error }}</li><!--ここを変更-->
    </ul><!--ここを変更-->
    {% endif %}<!--ここを変更-->

    <form method="POST" class="form" enctype="multipart/form-data">
        {% csrf_token %}
        <div class="file-upload-wrapper">
            <p>ファイルを選択<br>
                {{ form.testfile }}
            </p>
            <p>基準とする列名を入力<br>
                {{ form.columuns }}
            </p>
            <p>キーワードを入力<br>
                {{ form.code }}
            </p>
        </div>
        <p>ファイルをアップロードし、処理したファイルをダウンロードする</p>
        <button type="submit" class="save btn btn-outline-primary">実行</button>
    </form>
</div>

よろしくお願いいたします。

0

1Answer

やりたい事とは少々アプローチが変わりますが、
フォームのバリデーションをクライアント側でも行ってはいかがでしょうか?
HTML上でバリデーションを行うメリットは、
ユーザーがPOSTする前にチェックが走るため、サーバーと通信する前にチェックを走らせることができるため、
余計な通信が行われず、ページがリロードされずに済みます。

本題のサーバー側でのバリデーションですが、

try exceptで例外処理

上記が書いてありますが、こちらはうまくいかなかったのでしょうか?
考え方としては良いかと思いますが、
Django側でエラーを出すこと自体あまりよろしくありません。

Djangoのformにはis_validが存在するため、
バリデーションが正しくなければメッセージを記載したHTMLを返すように変更するように行えばOKです
具体的なコードはdjango validationで検索すれば色々ブログは出てくると思うので省略

1

Comments

  1. @kmnrk3131

    Questioner
    ご回答ありがとうございます。
    try exceptで例外処理してエラーメッセージのcontextを指定することで解決できました。

    今回対処するエラー内容が、アップされたcsvデータを読み込んでからでないと判別できないものでしたのでDjango側でエラーを出すことにしたのですが、こういった場合ですとDjango側からのエラーは仕方ないでしょうか?
  2. クライアントでチェックを行うことも可能ですが、
    実装するほどの手間でもなければ別に対応する必要はないかと
    (フォームの入力が多く、CSVで拒否されたときに再入力の手間が大きいとか)
  3. @kmnrk3131

    Questioner
    クライアントでチェックを行うことも可能なのですね!
    今回は入力値が少ないので、今後手間になりそうな際に調べて実装してみます。
    ご回答いただきありがとうございました。

Your answer might help someone💌