[Django]動画入力フォームでis_valid()がFalseになる現象について

## はじめに
Djangoを用いてユーザーがアップロードした動画を受け取るシステムを作成していた際に`is_valid()`が`True`にならない論理エラーが発生しました。本記事では`is_valid()`に関するエラーとその解決法について記述します。なお、使用しているDjangoのバージョンは`5.0.6`で、pythonのバージョンは`3.11.1`です。

## is_valid()とは
[django公式ドキュメント](https://docs.djangoproject.com/en/5.0/ref/forms/api/)によると、データが入力されて`form`のインスタンスに対して`is_valid()`はデータが正しいかどうかをブール値で返します。`form.is_valid()`とすることでformに対して検証を行うことができます。

## 関連コード
```python:vies.py
def file_upload(request):
    print("GGG")
    if request.method == 'POST':
        print("FFF")
        form = UploadFileForm(request.POST, request.FILES)
        print(form.is_valid())
        if form.is_valid():
            print("AAA")
            file_obj = request.FILES['file']
            if file_obj:
                print("BBB")
                handle_uploaded_file(file_obj)
                return render(request, 'file_upload/frontpage.html', {
                    'form': form, 'show_success_message': True
                })
            else:
                return render(request, 'file_upload/frontpage.html', {
                    'form': form, 'error_message': 'ファイルを選択していません。'
                })
    else:
        form = UploadFileForm()
    return render(request, 'file_upload/frontpage.html', {'form': form})
```
```python:forms.py
from django import forms

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()
```
```python:urls.py
from django.urls import path,include
from . import views

app_name = 'file_upload'

urlpatterns = [
    path('', views.file_upload, name='file_upload'),
    path('process/', views.processing, name='processing'),
    path('feedback/', include('feedback.urls')),
]
```
```html:frontpage.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ファイルアップロード</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        .header {
            text-align: left;
            font-size: 2em;
            margin: 20px;
        }
        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 80vh;
        }
        .file-title {
            font-size: 1.5em;
            margin-bottom: 20px;
        }
        .file-upload {
            margin-bottom: 20px;
        }
        .file-upload input {
            margin-bottom: 10px;
        }
        .process-btn {
            margin-top: 20px;
        }
        .error-message {
            color: red;
            margin-top: 10px;
        }
    </style>
    <script>
        function showSuccessMessage() {
            alert('ファイルが正常にアップロードされました！');
        }

        function showErrorMessage() {
            alert('ファイルを選択していません。');
        }
    </script>
</head>
<body>
    <div class="header">
        preai
    </div>
    <div class="container">
        <div class="file-title">
            ファイルを選択してください
        </div>
        <form id="uploadForm" method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            <div class="file-upload">
                {{ form.file }}
                <button type="submit">アップロード</button>
            </div>
            {% if error_message %}
                <div class="error-message">{{ error_message }}</div>
            {% endif %}
        </form>
        <div class="process-btn">
            <form action="{% url 'file_upload:processing' %}" method="get">
                <button type="submit">処理</button>
            </form>
        </div>
    </div>
    {% if show_success_message %}
        <script>
            showSuccessMessage();
        </script>
    {% endif %}
</body>
</html>
```
## 原因と解決策

### タイトルを受け取っていないのが原因
タイトルを動画の名前だと勘違いしておりタイトルを受け取るフォームを作っていませんでした。タイトルやファイルを受け取れている場合は出力にちゃんとその文字列が含まれるので勘違いしないように気を付けてください。`print(request.POST)`,`print(request.FILES`として表示した結果を以下に示します。

#### タイトルを受け取っていない場合の出力
```
フォームのデータ: <QueryDict: {'csrfmiddlewaretoken': ['bEDtPv2b7fUsZ1miO25yHG3rmqiFZqmdZo9MBgJZnYFoV8x9QiRXgiGPK4sj8QPo']}>
アップロードされたファイル: <MultiValueDict: {'file': [<TemporaryUploadedFile: demo.mp4 (video/mp4)>]}>
```

#### タイトルを受け取っている場合の出力
```
フォームのデータ: <QueryDict: {'csrfmiddlewaretoken': ['QvsPSGH9hL0TnTsPOozaaBoqdYneY3EdEfY8EroXxuLPj0DGQElzJd1OBCxS7t7o'], 'title': ['demo']}>
アップロードされたファイル: <MultiValueDict: {'file': [<TemporaryUploadedFile: demo.mp4 (video/mp4)>]}>
```
フォームのデータの最後に`title`タグが追加されていることが分かります。

### 解決策
タイトルを受け取っていないため`models.py`と`forms.py`を変更して完了。無事に動画を受け取れるようになりました。また、`models.py`で形式を指定して`forms.py`では`forms.ModelForm`として参照するように設定しました。
```python:forms.py
from django import forms
from file_upload.models import Post

class UploadFileForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('file', )

```

```python:models.py
from django.db import models

class Post(models.Model):
    file = models.FileField()

```


### 解決後のコード
いかにgithubのリンクを貼っておきます。今回のエラーはfile_uploadファイル内で起こっています。

https://github.com/HinataYasukawa/preaiwebapp

## 参照したサイト

https://docs.djangoproject.com/en/5.0/ref/forms/api/

https://qiita.com/ekzemplaro/items/07abd9a941bcd0eb5834