【Django】forms.ModelFormでフォームの作り方


概要

Djangoのforms.ModelFormで「ユーザーが投稿できるフォーム画面」を実装したいと思います。

前回と同様に、タイトルとメモを投稿するフォームになっています。(掲示板みたいな)

なお、環境はpython 3.7.3django 2.2になります。

1. プロジェクト作成

1-1. まずはプロジェクトファイルを作りたいので、下記のコマンドでディレクトリを作成します。

terminal
$ mkdir sampleform

1-2. 次にsampleformというプロジェクトを作成します。最後の.は正しいので、そのとおり実行してください。(ディレクトリはsimpleformであることを確認してください)

terminal
~sampleform$ django-admin startproject formproject .

1-3. コマンドを実行するとsampleformディレクトリには、下記のようにファイルが生成されます。

sampleform/
sampleform
├── formproject
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

2. アプリケーションを作成

2-1. 新しくアプリケーションを作成したいので、下記のコマンドを実行します。今回は、formappというアプリケーションです。

terminal
$ python manage.py startapp formapp

実行したらディレクトリは下記のようになると思います。

ざっくり3つに分けると、sampleformディレクトリ内に、①formappというアプリケーション、②formprojectというプロジェクト、③manage.pyファイルになります。

simpleform/
simpleform
├── formapp
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── formproject
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-37.pyc
│   │   └── settings.cpython-37.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

2-2. formprojectsettings.pyにあるINSTALLED_APPSに、formappを追加します。

formproject/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #追加
    'formapp',
]

2-3. formと投稿一覧のlistを表示できるようTEMPLATESにも設定をします。DIRS[BASE_DIR, 'templates'を追加します。

formproject/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        #追加
        'DIRS': [BASE_DIR, 'templates'],
        #ここまで
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

2-4. モデルの定義をmodels.pyに追加します。

formapp/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    memo = models.TextField()

2-5. 新しいモデルをデータベースに追加するので、makemigrationsmigrateを実行します。

terminal
$ python manage.py makemigrations
$ python manage.py migrate

3. Formを表示させよう

3-1. まずは、formproject/urls.pyに下記のように追加してください。

formproject/urls.py
from django.contrib import admin
#includeを追加
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    #追加
    path('', include('formapp.urls')),
]

3-2. formappurls.pyファイルを新規作成して、下記のコードを追加してください。これで、
https://サイト名.com/とアクセスすれば、投稿一覧のlistが表示され、
https://サイト名.com/formとアクセスすれば、新規投稿のformが表示され、
https://サイト名.com/details/番号とアクセスすれば、投稿の詳細であるdetailが表示されるようにします。

formapp/urls.py
from django.urls import path
from .views import listfunc, formfunc, detailfunc

urlpatterns = [
    path('', listfunc, name='list'),
    path('form/', formfunc, name='form'),
    path('detail/<int:pk>', detailfunc, name='detail'),
]

3-3. 後ほど作成するTemplatesにアクセスできるよう、views.pyに処理を追加してください。

formapp/views.py
from django.shortcuts import render, redirect
from django.utils import timezone
from .models import Post
from .forms import PostForm

def formfunc(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.save()
            return redirect('list')
    else:
        form = PostForm()
    return render(request, 'form.html', {'form': form})

def listfunc(request):
    posts = Post.objects.all()
    return render(request, 'list.html', {'posts': posts})

def detailfunc(request, pk):
    post = Post.objects.get(pk=pk)
    return render(request, 'detail.html', {'post': post})

3-4. formに必要なforms.pyファイルを、formappに新規作成して、下記のようにしてください。

formapp/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('title', 'memo')

3-5. Templatesフォルダーをsampleformに新規作成してください。その中に下記ファイルを作成し、それぞれコードを追加してください。
base.html
detail.html
form.html
list.html

sampleform/templates/base.html
<!DOCTYPE html>
<html>
  <head>
    <title>Post App</title>
  </head>
  <body>
    <a href="/">Home</a> | <a href="{% url 'form' %}">新規投稿</a>
    {% block content %}
    {% endblock %}
  </body>
</html>
sampleform/templates/detail.html
{% extends 'base.html' %}

{% block content %}
  <p>タイトル:{{ post.title }}</p>
  <p>メモ:{{ post.memo }}</p>
{% endblock content %}

sampleform/templates/form.html
{% extends 'base.html' %}

{% block content %}
  <h1>Create New Post</h1>
  <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">投稿する</button>
  </form>
{% endblock %}

sampleform/templates/list.html
{% extends 'base.html' %}

{% block content %}
  <p>-------------------</p>
  {% for post in posts %}
    タイトル:<a href="{% url 'detail' post.pk %}">{{ post.title }}</a>
    <p>-------------------</p>
  {% endfor %}
{% endblock content %}

ディレクトリは下記のようになるはずです。

sampleform/
sampleform
├── formapp
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── formproject
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-37.pyc
│   │   └── settings.cpython-37.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── templates
    ├── base.html
    ├── detail.html
    ├── form.html
    └── list.html

4. localhost起動

4-1. localhostを起動して、しっかりと表示されるか確認したいので、runserverを実行します。

terminal
$ python manage.py runserver

4-2. http://localhost:8000/をブラウザーで開くと、このようなフォームが表示されるはずです。(まだ何も投稿していないので、一覧に何もありません)

①「新規投稿」をクリックして、試しに投稿してみましょう。

②そうしたら一覧に、さきほどの投稿が表示されているはずです。

最後に「タイトル(画像ではtest)」をクリックしてみると...

③...しっかり詳細が表示されています!

このように一覧表示(list)投稿画面(form)詳細画面(detail)が動作していれば、正しく実行できています。

以上