0
0

More than 1 year has passed since last update.

Djangoを使ったCRUD機能実装&Herokuデプロイ

Last updated at Posted at 2021-12-12

概要

Django4がリリースされたこともあり、検証も兼ねて超初歩的なブログアプリを作成し、CRUD機能の実装からHerokuへのデプロイまでを一通り行ってみた。
説明内容に誤りがありましたら、ご教授いただけますと幸いです。

開発環境

  • Python=3.9.7
  • Django=4.0
  • PostgreSQL=13.5
  • psycopg2=2.8.6

*Heroku用に追加

  • django-heroku=0.3.1
  • gunicorn=20.1.0

アプリケーションの作成からindex.htmlを表示させるまで

プロジェクトの作成

$ django-admin startproject django_project
$ cd django_project
$ python manage.py startapp blog_app

作成したアプリの有効化

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog_app', # 作成したアプリ名を追加
]

アプリケーションで個別に設定したルーティングを大元のルーティングに適用させる(ここでは、blog_appのルーティングを適用させている)

[django_project/urls.py]

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog_app/', include('blog_app.urls')),
]

index.htmlの作成

index.htmlの作成
blog_app配下にtemplates/blog_appフォルダを作成し、その中にindex.htmlを作成する
[blog_app/templates/blog_app/index.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Index Page</h1>
    </body>

</html>

コントローラの設定
[blog_app/views.py]

from django.shortcuts import render


def index(request):
    return render(request, 'blog_app/index.html')

ルーティングの設定
[blog_app/urls.py]

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

サーバを起動し、http://localhost:8000/blog_app/にアクセスしてindex.htmlが表示されれば成功。

PostgreSQLのインストール

*ローカルにPostgreSQLがインストールされている前提です。
DjangoでPostgreSQLを使用するために、psycopg2と言うパッケージが必要です。
psycopg2のインストールします。(Macの場合、openssl必要/環境によってpsycopg2のインストール方法が異なるようなので、適宜変更してください。)

$ brew install openssl
$ echo 'export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH"' >> ~/.zshrc
$ source ~/.zshrc
$ pip install psycopg2==2.8.6
$ export LDFLAGS="-L/opt/homebrew/opt/curl/lib -L/opt/homebrew/opt/openssl/lib"
$ export CPPFLAGS="-I/opt/homebrew/opt/curl/include -I/user/local/opt/openssl/include"

ロールとデータベースの作成

$ psql postgres
postgres=# CREATE DATABASE django_project;
postgres=# CREATE USER USERNAME WITH PASSWORD 'password';

DjangoでPostgreSQLを使用する設定
[django_project/settings.py]

# 省略
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'django_project',
        'USER': 'ロール名',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}
#省略

サーバを起動し、ブラウザ上でアプリケーションを表示できれば成功

モデルの作成

テーブル定義

[blog_app/models.py]

from django.db import models

class Blog(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField(max_length=300)

マイグレーションファイルの作成

$ python manage.py makemigrations blog_app

マイグレーションの実行

$ python manage.py migrate

コントローラの設定
[blog_app/views.py]

from django.shortcuts import render
from django.http import HttpResponse
from .models import Blog

def index(request):
    data = Blog.objects.all()
    params = {
        'data': data,
    }
    return render(request, 'blog_app/index.html', params)

index.htmlを作成
[blog_app/templates/blog_app/index.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>index</h1>
        <table>
            <tr>
                <th>ID</th>
                <th>TITLE</th>
                <th>CONTENT</th>
            </tr>
            {% for item in data %}
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.content}}</td>
            </tr>
            {% endfor %}
        </table>
    </body>

</html>

サーバを起動し、ブラウザ上でアプリケーションを表示できれば成功

Create機能の追加

forms.pyを作成し、フォームの内容を記述
[blog_app/forms.py]

from django import forms

class BlogForm(forms.Form):
    title = forms.CharField(label='TITLE', max_length=100)
    content = forms.CharField(label='CONTENT', max_length=300)

create.htmlを作成
[blog_app/templates/blog_app/create.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Create</h1>
        <form action="{% url 'create' %}" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="click">
        </form>
    </body>

</html>

コントローラにcreate関数を作成
[blog_app/views.py]

from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import redirect
from .models import Blog
from .forms import BlogForm

def index(request):
    data = Blog.objects.all()
    params = {
        'data': data,
    }
    return render(request, 'blog_app/index.html', params)

def create(request):
    params = {
        'form': BlogForm(),
    }
    if (request.method == 'POST'):
        title = request.POST['title']
        content = request.POST['content']
        blog = Blog(title=title, content=content)
        blog.save()
        return redirect(to='/blog_app')
    return render(request, 'blog_app/create.html', params)

ルーティングを修正
[blog_app/urls.py]

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('create', views.create, name='create'),
]

サーバを起動し、ブラウザ上でアプリケーションを表示できれば成功

ModelFormの利用

[blog_app/forms.py]

from django import forms
from .models import Blog

class BlogForm(forms.ModelForm):
    class Meta:
        model = Blog
        fields = ['title', 'content']

[blog_app/views.py]

# 省略
def create(request):
    if (request.method == 'POST'):
        obj = Blog()
        blog = BlogForm(request.POST, instance=obj)
        blog.save()
        return redirect(to='/blog_app')
    params = {
        'form': BlogForm(),
    }
    return render(request, 'blog_app/create.html', params)

サーバを起動し、ブラウザ上でアプリケーションを表示できれば成功

Update機能の追加

ルーティングの追加
[blog_app/urls.py]

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('create', views.create, name='create'),
    path('edit/<int:num>', views.edit, name='edit'),
]

indexページに編集リンクを追加
[blog_app/templates/blog_app/index.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Index</h1>
        <table>
            <tr>
                <th>ID</th>
                <th>TITLE</th>
                <th>CONTENT</th>
            </tr>
            {% for item in data %}
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.content}}</td>
                <td><a href="{% url 'edit' item.id %}">Edit</a></td>
            </tr>
            {% endfor %}
        </table>
    </body>

</html>

blog_app/templates/blog_appに新たにedit.htmlを作成し、以下のコードを記述
[blog_app/templates/blog_app/edit.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Edit</h1>
        <form action="{% url 'edit' id %}" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="click">
        </form>
    </body>

</html>

edit関数の作成
[blog_app/views.py]

# 省略

def edit(request, num):
    obj = Blog.objects.get(id=num)
    if (request.method == 'POST'):
        blog = BlogForm(request.POST, instance=obj)
        blog.save()
        return redirect(to='/blog_app')
    params = {
        'id': num,
        'form': BlogForm(instance=obj),
    }
    return render(request, 'blog_app/edit.html', params)

サーバを起動し、ブラウザ上でアプリケーションを表示できれば成功

Delete機能の追加

ルーティングの追加
[blog_app/urls.py]

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('create', views.create, name='create'),
    path('edit/<int:num>', views.edit, name='edit'),
    path('delete/<int:num>', views.delete, name='delete'),
]

indexページに削除リンクを追加
[blog_app/templates/blog_app/index.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Index</h1>
        <table>
            <tr>
                <th>ID</th>
                <th>TITLE</th>
                <th>CONTENT</th>
            </tr>
            {% for item in data %}
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.content}}</td>
                <td><a href="{% url 'edit' item.id %}">Edit</a></td>
                <td><a href="{% url 'delete' item.id %}">Delete</a></td>
            </tr>
            {% endfor %}
        </table>
    </body>

</html>

blog_app/templates/blog_appに新たにdelete.htmlを作成し、以下のコードを記述
[blog_app/templates/blog_app/delete.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Delete</h1>
        <table>
            <tr>
                <th>ID</th>
                <td>{{obj.id}}</td>
            </tr>
            <tr>
                <th>TITLE</th>
                <td>{{obj.title}}</td>
            </tr>
            <tr>
                <th>CONTENT</th>
                <td>{{obj.content}}</td>
            </tr>
            <form action="{% url 'delete' id %}" method="post">
                {% csrf_token %}
                <tr>
                    <th>
                    <td><input type="submit" value="click"></td>
                    </th>
                </tr>
            </form>
        </table>
    </body>

</html>

delete関数の作成
[blog_app/views.py]

# 省略

def delete(request, num):
    blog = Blog.objects.get(id=num)
    if (request.method == 'POST'):
        blog.delete()
        return redirect(to='/blog_app')
    params = {
        'id': num,
        'obj': blog,
    }
    return render(request, 'blog_app/delete.html', params)

Read機能の追加

ルーティングの追加
[blog_app/urls.py]

from django.urls import path
from . import views


urlpatterns = [
    path('', views.index, name='index'),
    path('create', views.create, name='create'),
    path('edit/<int:num>', views.edit, name='edit'),
    path('delete/<int:num>', views.delete, name='delete'),
    path('detail/<int:num>', views.detail, name='detail'),
]

indexページに詳細リンクを追加
[blog_app/templates/blog_app/index.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Index</h1>
        <table>
            <tr>
                <th>ID</th>
                <th>TITLE</th>
                <th>CONTENT</th>
            </tr>
            {% for item in data %}
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.content}}</td>
                <td><a href="{% url 'detail' item.id %}">Detail</a></td>
                <td><a href="{% url 'edit' item.id %}">Edit</a></td>
                <td><a href="{% url 'delete' item.id %}">Delete</a></td>
            </tr>
            {% endfor %}
        </table>
    </body>

</html>

blog_app/templates/blog_appに新たにdetail.htmlを作成し、以下のコードを記述
[blog_app/templates/blog_app/detail.html]

<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="utf-8">
        <title>blog_app</title>
    </head>

    <body>
        <h1>Detail</h1>
        <table>
            <tr>
                <th>ID</th>
                <td>{{obj.id}}</td>
            </tr>
            <tr>
                <th>TITLE</th>
                <td>{{obj.title}}</td>
            </tr>
            <tr>
                <th>CONTENT</th>
                <td>{{obj.content}}</td>
            </tr>
        </table>
    </body>

</html>

detail関数の作成
[blog_app/views.py]

# 省略

def detail(request, num):
    blog = Blog.objects.get(id=num)
    params = {
        'id': num,
        'obj': blog,
    }
    return render(request, 'blog_app/detail.html', params)

Herokuへのデプロイ

公式:https://devcenter.heroku.com/ja/articles/django-app-configuration

設定

django-herokuとgunicornをインストール

$ pip install django_heroku gunicorn

Procfileruntime.txtrequirements.txtの3つのファイルを作成する

Image from Gyazo

1. Procfileの作成

web: gunicorn django_project.wsgi

2. runtime.txtの作成

python-3.9.7

3. プロジェクトディレクトリで以下のコマンドを実行し、requirements.txtを作成

$ pip freeze > requirements.txt

[django_project/settings.py]

from pathlib import Path
import django_heroku   # 追記

# 省略

# Activate Django-Heroku.
django_heroku.settings(locals())   # 最終行に追記

コミット&デプロイ

$ git add .
$ git commit "add heroku setting"
$ heroku create
$ heroku config:set DISABLE_COLLECTSTATIC=1
$ git push heroku master
$ heroku run python manage.py migrate
$ heroku open

heroku openコマンドで起動する画面でのページは存在しないため、「Not Found The requested resource was not found on this server.」が表示される。
https://xxxxx-xxxxx-xxxxx.herokuapp.com/blog_appパスを追加してアクセスすれば、アプリが表示されるはずです。

アセット関連については別途設定が必要そう(今回アセットについては未検証)

次回

2. 1対多のアソシエーション実装&確認ダイアログ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0