【Python】サンプルプロジェクトのテンプレート【Django】

2023年8月5日Python

サンプルプロジェクトのテンプレート


Djangoで簡単なアプリケーションを開発するためのテンプレート

アプリケーション概要

  • Web画面からデータベースにテキストを登録する
  • 登録するユーザーを管理する

テキスト中の[projectname] [appname]について

  • それぞれ任意のプロジェクト名、アプリケーション名を付ける
  • [ ]は視認性のためなのでコマンドや設定ファイルでは不要
  • ここではsampleproject、sampleappで作業を行う

1.開発環境を構築する

仮想環境を作成する

PythonのvenvコマンドでDjango用の仮想環境を作成する

python -m venv .env_django

ライブラリをインストールする

作成したPythonの仮想環境にDjangoのライブラリをインストールする

pip install django

2.プロジェクトを作成する

django-admin コマンドでプロジェクトを作成する

django-admin startproject [projectname]
  • Djangoのプロジェクトを作成する
  • 仮想環境に作成するのでvenvが起動している状態で実行する

プロジェクト初期のファイル構成

  • [projectname] フォルダ
    • __init__.py
    • asgi.py
    • settings.py
    • urls.py
    • wsgi.py
  • manage.py

初期の雛形ファイルを用いてローカルサーバを起動する

コマンドでローカルサーバを起動する

python manage.py runserver
  • ワーニングが出る場合があるがこの時点では問題ない
  • ブラウザから http://127.0.0.1:8000/ にアクセスする
  • インストール成功のページが表示されていることを確認する

プロジェクト設定を変更する

[projectname]/settings.py

変更箇所 変更前 変更後
LANGUAGE_CODE en-us ja
TIME_ZONE UTC Asia/Tokyo
  • 保存後にブラウザを更新すると日本語表記になる

3.データベースをセットアップする

テーブルを作成する

コマンドでマイグレーションを実行しテーブルを作成する

python manage.py migrate

実行結果

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  (中略)
  Applying sessions.0001_initial... OK

4.アプリケーションを作成する

機能の最小単位となるアプリケーションを作成する

プロジェクトの中にアプリケーションの雛形を作成する

python manage.py startapp [appname]

この時点でのファイル構成

  • [appname] フォルダ
  • [projectname] フォルダ
  • db.sqlite3
  • manage.py

プロジェクト設定を変更する

[projectname]/settings.py

  • '[appname].apps.[Appname]Config', の形式でアプリケーション設定を追加する
変更箇所 変更前 変更後
INSTALLED_APPS (末尾に追加する) 'sampleapp.apps.SampleappConfig',

5.トップページを表示する

ビュー関数を定義する

[appname]/views.py

from django.http import HttpResponse

def top(request):
    return HttpResponse(b'

Hello World from app/views.py

')

ルーティングを設定する

[projectname]/urls.py

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

from sampleapp.views import top

urlpatterns = [
    path('', top, name='top'),
    path('admin/', admin.site.urls),
]

django.urls.path の引数について

  1. HTTPリクエストパス
    1. ここでは '/' へのアクセスに対してtop関数を呼び出す設定
    2. 先頭の / は省略するため空白を指定している
  2. ビュー関数(views.py のtop関数)
  3. URLの逆引き時に利用するキーワード引数

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/ にアクセスする
  • トップページへのアクセスに対して view.py のtop関数の内容が返却されることを確認する

追加のルーティング設定をする

以下のページを作成する想定でルーティングを設定しておく

ページ 内容
top 一覧画面
sample_new 登録画面
sample_edit 編集画面
sample_detail 詳細画面

[appname]/views.py

from django.http import HttpResponse

def top(request):
    return HttpResponse(b'

Hello World from app/views.py

') def sample_new(request): return HttpResponse('登録画面') def sample_edit(request, sample_id): return HttpResponse('編集画面') def sample_detail(request, sample_id): return HttpResponse('詳細画面')

[appname]/urls.py を新規作成する

from django.urls import path

from sampleapp import views

urlpatterns = [
    path('new/', views.sample_new, name='sample_new'),
    path('/', views.sample_detail, name='sample_detail'),
    path('/edit/', views.sample_edit, name='sample_edit'),
]

[projectname]/urls.py

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

from sampleapp.views import top

urlpatterns = [
    path('', top, name='top'),
    path('sampleapp/', include('sampleapp.urls')),
    path('admin/', admin.site.urls),
]

6.データベースと連携する

モデルを定義する

[appname]/models.py

from django.conf import settings
from django.db import models

class Sample(models.Model):
    title = models.CharField('タイトル', max_length=128)
    text = models.TextField('テキスト', blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='登録者', on_delete=models.CASCADE)
    created_at = models.DateTimeField('登録日', auto_now_add=True)
    updated_at = models.DateTimeField('更新日', auto_now=True)

    def __str__(self):
        return self.title

データベースをマイグレーションする

マイグレーションを作成する

python manage.py makemigrations

実行結果

Migrations for 'sampleapp':
sampleapp\migrations
Migrations for 'sampleapp':
sampleapp\migrations\0001_initial.py
  - Create model Sample
01_initial.py - Create model Sample

マイグレーションをテーブルに反映する

python manage.py migrate

実行結果

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sampleapp, sessions
Running migrations:
  Applying sampleapp.0001_initial... OK

7.管理画面を利用する

管理画面からのテーブル操作を有効にする

[appname]/admin.py

from django.contrib import admin
from sampleapp.models import Sample

admin.site.register(Sample)

ユーザを作成する

管理者となるadmin権限を持つユーザを作成する

python manage.py createsuperuser

実行結果

Username (leave blank to use 'username'): admin
Email address: admin@sample.com
Password:
Password (again):
Superuser created successfully.

管理画面にログインする

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/admin/ にアクセスする
  • 作成したadmin権限アカウントでログインできることを確認する

8.データベースを操作する

Pythonのシェルからデータベースを操作する

シェルを起動する

python manage.py shell

データを取得する

作成したユーザ情報を取得する

from django.contrib.auth.models import User
users = User.objects.all()
users

実行結果

<QuerySet [<User: admin>]>

IDを指定してユーザデータを取得する

User.objects.get(id=1)

実行結果

<User: admin>

ユーザ名を指定してユーザデータを取得する

User.objects.get(username='admin')

実行結果

<User: admin>

データを作成する

モデル定義に合わせてデータを作成する

from sampleapp.models import Sample
admin_user = User.objects.get(id=1)
sample = Sample(title='コマンドからのテスト入力',
                text='コマンドからのテスト入力です',
                created_by=admin_user)

作成された項目を表示する

sample.title

実行結果

'コマンドからのテスト入力'

自動採番項目はsaveメソッドを実行しないと反映されない

sample.id

実行結果

>>>(表示なし)

saveを実行して再度確認する

sample.save()
sample.id

実行結果

1

データを更新する

id=1のtitle属性を更新する

sample = Sample.objects.get(id=1)
sample.title = 'コマンドからの更新テスト'
sample.save()
Sample.objects.all()

実行結果

<QuerySet [<Sample: コマンドからの更新テスト>]>

データを削除する

deleteメソッドでデータを削除する

sample.delete()

実行結果

(1, {'sampleapp.Sample': 1})

削除の確認をする

Sample.objects.all()

実行結果

<QuerySet []>

9.トップページをHTMLファイルで表示する

HTMLファイルを作成する

[appname]フォルダ配下にHTMLファイル用のフォルダを作成する

  • [appname]/templates/[appname]

sampleapp/templates/sampleapp/top.html

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappのトップページ</title>
</head>

<body>
  <p>Hello World from sampleapp/templates/sampleapp/top.html</p>
</body>

</html>

ルーティングの設定をする

[appname]/views.py

  • top関数のみrender関数を使用する
from django.http import HttpResponse
from django.shortcuts import render

def top(request):
    return render(request, 'sampleapp/top.html')

def sample_new(request):
    return HttpResponse('登録画面')

def sample_edit(request, sample_id):
    return HttpResponse('編集画面')

def sample_detail(request, sample_id):
    return HttpResponse('詳細画面')

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/ にアクセスする
  • top.html の内容が表示されていることを確認する

10.トップページにデータベースの情報を表示する

ビュー関数を設定する

[appname]/views.py

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

from sampleapp.models import Sample

def top(request):
    sampleapp = Sample.objects.all()
    context = {'sampleapp': sampleapp}
    return render(request, 'sampleapp/top.html', context)

def sample_new(request):
    return HttpResponse('登録画面')

def sample_edit(request, sample_id):
    return HttpResponse('編集画面')

def sample_detail(request, sample_id):
    return HttpResponse('詳細画面')

top.htmlファイルを編集する

[appname]/templates/[appname]/top.html

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappのトップページ</title>
</head>

<body>
  <h1>Django sampleproject のトップページ</h1>
  <h2>DB登録情報</h2>
  {% if sampleapp %}
  <table class="table">
    <thead>
      <tr>
        <th>登録者</th>
        <th>登録日</th>
        <th>タイトル</th>
      </tr>
    </thead>
    <tbody>
      {% for sample in sampleapp %}
      <tr>
        <th>{{ sample.created_by.username }}</th>
        <th>{{ sample.created_at }}</th>
        <th><a href="{% url 'sample_detail' sample.id %}">{{ sample.title }}</a></th>
      </tr>
      {% endfor %}
    </tbody>
  </table>
  {% else %}
  <p>登録情報がありません</p>
  {% endif %}
</body>

</html>

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/ にアクセスする
  • DBの登録内容が表示されていることを確認する

11.詳細画面を表示する

ビュー関数を設定する

[appname]/views.py

from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404

from sampleapp.models import Sample

def top(request):
    sampleapp = Sample.objects.all()
    context = {'sampleapp': sampleapp}
    return render(request, 'sampleapp/top.html', context)

def sample_new(request):
    return HttpResponse('登録画面')

def sample_edit(request, sample_id):
    return HttpResponse('編集画面')

def sample_detail(request, sample_id):
    sample = get_object_or_404(Sample, pk=sample_id)
    return render(request, 'sampleapp/sample_detail.html', {'sample': sample})

htmlファイルを作成する

[appname]/templates/[appname]/sample_detail.html

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappの詳細画面</title>
</head>

<body>
  <div>
    <h1>{{ sample.title }} by {{ sample.created_by.username }}</h1>
    <h2>登録日: {{ sample.created_at }}</h2>
    <h2>{{ sample.text }}</h2>
  </div>
</body>

</html>

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/sampleapp/1/ にアクセスする
    • 末尾の数字は自動採番されているDBの登録番号なので状況により異なる
  • 作成したHTMLファイルの構成でDBの登録内容が表示されていることを確認する

12.登録・編集画面を表示する

HTML検証用のフォームファイルを作成する

[appname]/forms.py

from django import forms

from sampleapp.models import Sample

class SampleForm(forms.ModelForm):
    class Meta:
        model = Sample
        fields = ('title', 'text')

ビュー関数を設定する

[appname]/views.py

from django.contrib.auth.decorators import login_required
from django.http import HttpResponseForbidden
from django.shortcuts import render, redirect, get_object_or_404

from sampleapp.forms import SampleForm
from sampleapp.models import Sample

def top(request):
    sampleapp = Sample.objects.all()
    context = {'sampleapp': sampleapp}
    return render(request, 'sampleapp/top.html', context)

@login_required
def sample_new(request):
    if request.method == 'POST':
        form = SampleForm(request.POST)
        if form.is_valid():
            sample = form.save(commit=False)
            sample.created_by = request.user
            sample.save()
            return redirect(sample_detail, sample_id=sample.pk)
    else:
        form = SampleForm()
    return render(request, 'sampleapp/sample_new.html', {'form': form})

@login_required
def sample_edit(request, sample_id):
    sample = get_object_or_404(Sample, pk=sample_id)
    if sample.created_by_id != request.user.id:
        return HttpResponseForbidden("このテキストの編集は許可されていません。")

    if request.method == "POST":
        form = SampleForm(request.POST, instance=sample)
        if form.is_valid():
            form.save()
            return redirect('sample_detail', sample_id=sample_id)
    else:
        form = SampleForm(instance=sample)
    return render(request, 'sampleapp/sample_edit.html', {'form': form})

def sample_detail(request, sample_id):
    sample = get_object_or_404(Sample, pk=sample_id)
    return render(request, 'sampleapp/sample_detail.html', {'sample': sample})

[appname]/templates/[appname]/sample_new.html

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappの登録画面</title>
</head>

<body>
  <h1>登録フォーム</h1>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
  </form>
</body>

</html>

[appname]/templates/[appname]/sample_edit.html

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappの編集画面</title>
</head>

<body>
  <h1>編集フォーム</h1>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">保存</button>
  </form>
</body>

</html>

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • 登録画面
    • ログイン状態でブラウザから http://127.0.0.1:8000/sampleapp/new/ にアクセスする
    • 作成したHTMLファイルの構成で表示されていることを確認する
  • 編集画面
    • ログイン状態でブラウザから http://127.0.0.1:8000/sampleapp/1/edit/ にアクセスする
    • 作成したHTMLファイルの構成でDBの登録内容が表示されていることを確認する

13.見た目を整える

CSSを適用する

Bootstrapをインストールする

pip install django-bootstrap5

プロジェクト設定を変更する

[projectname]/settings.py

変更箇所 変更前 変更後
INSTALLED_APPS (末尾に追加する) 'django_bootstrap5',
TEMPLATES [], [BASE_DIR / 'templates'],

HTMLファイルを変更する

[appname]/templates/[appname]/top.html

{% load django_bootstrap5 %}

<html>

<head>
  <meta charset="utf-8">
  <title>sampleappのトップページ</title>
  {% bootstrap_css %}
  {% bootstrap_javascript %}
</head>

<body>
  <nav class="navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
      <a href="/" class="navbar-brand">sampleapp</a>
    </div>
  </nav>

  <main>
    <div class="container">
      <div class="welcome">
        <h1>Django sampleproject のページ</h1>
        <a class="btn btn-primary" href="{% url 'sample_new' %}">テキストの新規登録</a>
      </div>

      <h3>DB登録情報</h3>
      {% if sampleapp %}
      <table class="table">
        <thead>
          <tr>
            <th>登録者</th>
            <th>登録日</th>
            <th>タイトル</th>
          </tr>
        </thead>
        <tbody>
          {% for sample in sampleapp %}
          <tr>
            <th>{{ sample.created_by.username }}</th>
            <th>{{ sample.created_at }}</th>
            <th><a href="{% url 'sample_detail' sample.id %}">{{ sample.title }}</a></th>
          </tr>
          {% endfor %}
        </tbody>
      </table>
      {% else %}
      <p>登録情報がありません</p>
      {% endif %}
    </div>
  </main>
</body>

</html>

CSSファイルを適用する

[appname]/static/[appname]/css/style.css

main {
  padding: 2rem 0;
}

.welcome {
  padding: 3rem 0;
  margin-bottom: 2rem;
  background-color: #eee;
  align-items: center;
  display: flex;
  flex-direction: column;
}

.snippet-date {
  margin-bottom: 1rem;
}

.source-code>.highlight {
  padding: 1rem;
  margin-bottom: 1rem;
}

14.HTMLの部品を共通化する

ベースとなるHTMLファイルを作成する

[projectname]/templates/base.html

  • templates フォルダは [appname] [projectname] と同じ階層
{% load static %}
{% load django_bootstrap5 %}
<html>

<head>
  <meta charset="utf-8">
  <title>sampleapp</title>
  {% bootstrap_css %}
  {% bootstrap_javascript %}
  <link rel="stylesheet" href="{% static 'sampleapp/css/style.css' %}">
  {% block extraheader %}{% endblock %}
</head>

<body>
  <nav class="navbar navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
      <a href="/" class="navbar-brand">sampleapp</a>
    </div>
  </nav>
  <main>
    <div class="container">
      {% block main %}{% endblock %}
    </div>
  </main>
</body>

</html>

base.htmlを継承するようにHTMLファイルを変更する

[appname]/templates/[appname]/top.html

{% extends "base.html" %}

{% block main %}
<div class="welcome">
  <h1 class="title">Django sampleproject のページ</h1>
  <a class="btn btn-primary" href="{% url 'sample_new' %}">テキストの新規登録</a>
</div>

<h3>DB登録情報</h3>

{% if sampleapp %}
<table class="table">
  <thead>
    <tr>
      <th>登録者</th>
      <th>登録日</th>
      <th>タイトル</th>
    </tr>
  </thead>
  <tbody>
    {% for sample in sampleapp %}
    <tr>
      <th>{{ sample.created_by.username }}</th>
      <th>{{ sample.created_at }}</th>
      <th><a href="{% url 'sample_detail' sample.id %}">{{ sample.title }}</a></th>
    </tr>
    {% endfor %}
  </tbody>
</table>
{% else %}
<p>登録情報がありません</p>
{% endif %}
{% endblock %}

[appname]/templates/[appname]/sample_new.html

{% extends "base.html" %}
{% load django_bootstrap5 %}

{% block main %}
<h2>sampleappの登録画面</h2>
<form method="post">
  {% csrf_token %}
  {% bootstrap_form form %}
  {% bootstrap_button button_type="submit" content="登録" %}
</form>
{% endblock %}

[appname]/templates/[appname]/sample_edit.html

{% extends "base.html" %}
{% load django_bootstrap5 %}

{% block main %}
<h2>sampleappの編集画面</h2>
<form method="post">
  {% csrf_token %}
  {% bootstrap_form form %}
  {% bootstrap_button button_type="submit" content="保存" %}
</form>
{% endblock %}

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver

15.アカウント管理機能を実装する

アプリケーションを作成する

コマンドの実行

python manage.py startapp accounts

ルーティングの設定をする

[projectname]/settings.py

変更箇所 変更前 変更後
INSTALLED_APPS (末尾に追加する) 'accounts.apps.AccountsConfig',

accounts/urls.py を新規作成する

from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path

urlpatterns = [
    path('login/', LoginView.as_view(
        redirect_authenticated_user=True,
        template_name='accounts/login.html'
    ), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]
  • Django提供のクラスベースビュー(LoginView, LogoutView)を利用してビューの実装を省略する

[projectname]/urls.py

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

from sampleapp.views import top

urlpatterns = [
    path('', top, name='top'),
    path('sampleapp/', include('sampleapp.urls')),
    path('admin/', admin.site.urls),
    path('accounts/', include('accounts.urls')),
]

ログイン用のページを作成する

accounts/templates/accounts/login.html を新規作成する

{% extends "base.html" %}
{% load django_bootstrap5 %}

{% block main %}
<h2>ログイン</h2>

<form method="post">
  {% csrf_token %}
  <input type="hidden" name="next" value="{{ next }}" />
  {% bootstrap_form form %}
  {% bootstrap_button button_type="submit" content="ログイン" %}
</form>
{% endblock %}

ログイン・ログアウト用のリンクを追加する

[projectname]/templates/base.html

{% load static %}
{% load django_bootstrap5 %}
<html>

<head>
  <meta charset="utf-8">
  <title>sampleapp</title>
  {% bootstrap_css %}
  {% bootstrap_javascript %}
  <link rel="stylesheet" href="{% static 'sampleapp/css/style.css' %}">
  {% block extraheader %}{% endblock %}
</head>

<body>
  <nav class="navbar navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
      <a href="/" class="navbar-brand">sampleapp</a>
      <ul class="navbar-nav mr-md-2">
        {% if user.is_authenticated %}
        <li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">ログアウト</a></li>
        {% else %}
        <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
        {% endif %}
      </ul>
    </div>
  </nav>
  <main>
    <div class="container">
      {% block main %}{% endblock %}
    </div>
  </main>
</body>

</html>

プロジェクト設定を変更する

[projectname]/settings.py

  • ファイル末尾に以下を追加する
# ユーザー認証
#
LOGIN_URL = '/accounts/login/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver
  • ブラウザから http://127.0.0.1:8000/accounts/login/ にアクセスする
  • 確認事項
    • ログインのリンクから既存のアカウントでログインできること
    • ログイン後にログアウトボタン(ヘッダ右側)からログアウトできる
    • ログアウト状態で新規登録ボタンをクリックするとログイン画面にリダイレクトされる

16.ユーザー登録機能の実装

パスを設定する

accounts/urls.py

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from django.views.generic import CreateView

urlpatterns = [
    path('signup/', CreateView.as_view(
        template_name='accounts/signup.html',
        form_class=UserCreationForm,
        success_url='/',
    ), name='signup'),
    path('login/', LoginView.as_view(
        redirect_authenticated_user=True,
        template_name='accounts/login.html'
    ), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

ユーザー登録用のページを作成する

accounts/templates/accounts/signup.html を新規作成する

{% extends "base.html" %}
{% load django_bootstrap5 %}

{% block main %}
<h2>ユーザー登録</h2>
<form method="post">
  {% csrf_token %}
  {% bootstrap_form form %}
  {% bootstrap_button button_type="submit" content="登録" %}
</form>
{% endblock %}

ユーザー登録用のリンクを追加する

[projectname]/templates/base.html

{% load static %}
{% load django_bootstrap5 %}
<html>

<head>
  <meta charset="utf-8">
  <title>sampleapp</title>
  {% bootstrap_css %}
  {% bootstrap_javascript %}
  <link rel="stylesheet" href="{% static 'sampleapp/css/style.css' %}">
  {% block extraheader %}{% endblock %}
</head>

<body>
  <nav class="navbar navbar-expand flex-md-row navbar-dark bg-dark">
    <div class="container justify-content-between">
      <a href="/" class="navbar-brand">sampleapp</a>
      <ul class="navbar-nav mr-md-2">
        {% if user.is_authenticated %}
        <li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">ログアウト</a></li>
        {% else %}
        <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
        <li class="nav-item"><a class="nav-link" href="{% url 'signup' %}">ユーザー登録</a></li>
        {% endif %}
      </ul>
    </div>
  </nav>
  <main>
    <div class="container">
      {% block main %}{% endblock %}
    </div>
  </main>
</body>

</html>

ブラウザから確認する

ローカルサーバを起動する

python manage.py runserver

Python

Posted by junichi