開発環境

この記事の開発環境は下記になります。

macOS sonoma
$ python --version
Python 3.11.4
$ sqlite3 -version
3.39.5

Djangoのインストール

Djangoのインストールがまだならインストールしましょう。

$ python -m pip install Django

この記事で使用するバージョンは下記です。

$ python -m django --version
5.0.4

プロジェクト作成

適当なディレクトリに移動したら下記コマンドでプロジェクトファイルを作成します。

$ django-admin startproject django_simple_cms

django_simple_cmsというディレクトリが作られるので移動します。

$ cd django_simple_cms

作成されたdjango_simple_cmsの中は次のようなファイル構成となります。

django_simple_cms/
├ manage.py
└ django_simple_cms/
    ├ __init__.py
    ├ settings.py
    ├ urls.py
    ├ asgi.py
    └ wsgi.py

Djangoの開発用サーバーを使用して動作確認をしてみましょう。

$ python manage.py runserver

コマンドを実行すると次のメッセージが表示されます。

Django version 5.0.4, using settings 'django_simple_cms.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

ブラウザでhttp://localhost:8000にアクセスしてみましょう。

画面に「The install worked successfully! Congratulations!」というメッセージが表示されたら正常に動作しています。

初期設定

日本語環境で使えるように初期設定を行います。
settings.pyの下記項目を編集してください。

django_simple_cms/settings.py

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

データベースの設定は同じファイルのDATABASESという項目で設定しますが、今回はデフォルト設定のSQLiteを使用しますのでそのまま使用します。

django_simple_cms/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

日本語にしたのでブラウザで確認すると今度は「インストールは成功しました!おめでとうございます!」というメッセージに変わっています。

Djangoスタート画面

ライブラリのインストール

開発するのに便利なライブラリをインストールします。

Django Debug Toolbar

このライブラリはSQLとかリクエストの値を画面に表示してくれます。
pipでインストールしましょう。

pip install django-debug-toolbar

設定ファイルに下記を追記します。

django_simple_cms/settings.py

INSTALLED_APPS = [
    # ...
    'debug_toolbar',
]

MIDDLEWARE = [
    # ...
    'debug_toolbar.middleware.DebugToolbarMiddleware',
]

# 追加
DEBUG_TOOLBAR_CONFIG = {
    'SHOW_TOOLBAR_CALLBACK' : lambda request: True,
}

ルーティングにも追記します。

django_simple_cms/urls.py

urlpatterns = [
    # ...
    path('__debug__/', include('debug_toolbar.urls')),
]

Django Debug Toolbarは後ほど解説するテンプレートを作成した時点で確認できるようになります。

postsアプリケーションの作成

Djangoは一つのプロジェクトの中に複数のアプリケーションを作成します。
はじめにお知らせを管理するpostsアプリケーションを作成しましょう。
次のコマンドを実行してください。

$ python manage.py startapp posts

django_simple_cmsディレクトリ以下に下記のファイルが生成されます。

posts/
├ __init__.py
├ admin.py
├ apps.py
├ migrations/
    └ __init__.py
├ models.py
├ tests.py
└ views.py

ビューの編集

DjangoはMVCではなくMTV(Model・Template・View)というアーキテクチャになります。
一般的なMVCのControllerにあたる部分がViewと思っていいのではないかと思います。

単純にテキストを表示する処理を作成してみましょう。
posts/views.pyを下記のように編集してみてください。

posts/views.py

from django.http import HttpResponse


def index(request):
    return HttpResponse('お知らせページ')

ルートの編集

作成したビューを表示するようにpostsのルートを編集します。

posts/urls.py

from django.urls import path

from . import views

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

編集したルートファイルを読み込めるように、プロジェクトのルートファイルを編集します。
/postsでアクセスした時postsアプリケーションのルートを読み込むように設定をします。

django_simple_cms/urls.py

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

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

ブラウザでhttp://localhost:8000/postsにアクセスしてみましょう。
ビューで設定した「お知らせページ」という文字が表示されます。

マイグレーションの作成

データベースにテーブルを作成します。
Djangoはモデルに設定するとマイグレーションファイルを作成する機能があるのでモデルから作成していきましょう。
posts/models.pyを下記のように修正します。

posts/models.py

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    is_public = models.BooleanField(default=True)
    published_at = models.DateTimeField()

できたら下記コマンドを実行することでマイグレーションファイルが作成されます。

$ python manage.py makemigrations posts

posts/migrations/0001_initial.pyというファイルが作られたことを確認してください。
マイグレーションファイルはPythonファイルですが、次のコマンドでどのようなSQLが実行されるのか確認することができます。

$ python manage.py sqlmigrate posts 0001

出力されたSQLをフォーマットすると下記のようになります。

BEGIN;

--
-- Create model Post
--
CREATE TABLE "posts_post" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "title" varchar(255) NOT NULL,
    "body" text NOT NULL,
    "is_public" bool NOT NULL,
    "published_at" datetime NOT NULL
);

COMMIT;

問題なさそうなのでmigrateコマンドを実行しましょう。

$ python manage.py migrate

これでDBに必要なテーブルが作成されます。確認してみましょう。

データベースの確認

sqlite3コマンドでDBに接続してもいいですが、dbshellコマンドでDBに接続することもできます。

$ python manage.py dbshell

テーブル一覧の確認

sqlite> .table

posts_postテーブルのスキーマ確認

sqlite> .schema posts_post

マイグレーションファイルを確認したときと同じカラム情報が表示されるはずです。
sqliteを終了します。

sqlite> .quit

管理ユーザーの作成

今回はDjangoのデフォルト管理画面を使用してデータを登録します。
管理画面にログインする為のユーザの作成を行いましょう。
下記コマンドを実行してください。

$ python manage.py createsuperuser

ユーザー名、メールアドレス、パスワードをそれぞれ入力します。

$ ユーザー名: admin
$ メールアドレス: admin@example.com
$ Password: **********
$ Password (again): *********

下記が表示されれば登録完了です。

Superuser created successfully.

http://localhost:8000/adminにアクセスするとログイン画面が表示されるので、登録した情報を入力します。

ログインに成功すると管理画面に遷移して下記画像のように表示されます。

Django管理画面

管理画面のカスタマイズ

初期状態では「認証と認可」の項目のみ表示されています。
先ほど作成したPostsも管理画面で編集できるように表示してみましょう。
posts/admin.pyを下記のように編集します。

posts/admin.py

from django.contrib import admin

from .models import Post

admin.site.register(Post)

これでPostsという項目が追加されますが英語なので日本語表記します。
posts/apps.pyverbose_nameに表示したい名称を設定します。

posts/apps.py

from django.apps import AppConfig


class PostsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'posts'
    verbose_name = '投稿'

posts/models.pyverbose_nameにも同じように設定します。

posts/models.py

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255, verbose_name='タイトル')
    body = models.TextField(verbose_name='内容')
    is_public = models.BooleanField(default=True, verbose_name='公開')
    published_at = models.DateTimeField(verbose_name='公開日')

    class Meta:
        verbose_name = '投稿'
        verbose_name_plural = '投稿'

再度管理画面を確認すると「投稿」という項目が増えて入力項目も日本語になっています。

一覧画面カスタマイズ

一覧ページの項目が「Post object (1)」のような表記になりわかりずらいのでタイトルと公開日を表示するようにします。
ついでにタイトルと内容で検索できるようにもしてみましょう。
posts/admin.pyを下記のように編集します。

posts/admin.py

from django.contrib import admin

from .models import Post

class PostAdmin(admin.ModelAdmin):
    list_display  = ['title', 'published_at']
    search_fields = ['title', 'body']

admin.site.register(Post, PostAdmin)

list_displayが一覧に表示する項目で、search_fieldsが検索に使用する項目です。
ブラウザで確認すると下記のように表示されます。

Django管理画面日本語表示

「投稿を追加」ボタンをクリックしていくつか登録してみましょう。

フロント一覧画面の作成

管理画面からデータの登録ができたので、フロント画面を作成して表示してみましょう。

posts/views.pyIndexViewを下記のように修正します。
データの一覧ページはgeneric.ListViewクラスを継承することで簡単に作ることができます。

posts/views.py

from django.http import HttpResponse
from django.views import generic

from .models import Post


class IndexView(generic.ListView):
    model = Post

このビューにアクセスできるようにルーティングも編集します。

posts/urls.py

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

次にテンプレートを作成します。
head要素など全ページ共通で使用する部分を書くレイアウトファイルになります。

posts/templates/base.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>株式会社Sample</title>
</head>
<body>
    {% block main %}
    {% endblock %}
</body>
</html>

block mainの部分に各ページのテンプレート内容を表示するように指定します。

一覧表示部分を作成していきましょう。
generic.ListViewを継承したクラスでは自動的に「モデル名_list.html」ファイルが読み込まれます。
なので今回はpost_list.htmlというファイル名で作成します。
またモデルから取得したデータはpost_listという変数に入っているので展開しましょう。

posts/templates/posts/post_list.html

{% extends "base.html" %}

{% block main %} 
<h1>お知らせ</h1>
{% if post_list %}
    <ul>
        {% for post in post_list %}
            <li>{{ post.title }}</li>
        {% endfor %}
    </ul>
{% else %}
    <p>お知らせはありません。</p>
{% endif %}
{% endblock %}

ブラウザで確認すると管理画面で入力した内容が表示されます。
ただ今の状態だと昇順で全件表示されてしまいますね。
下記条件で表示させてみましょう。

  • 公開日(published_at)で降順
  • 10件
  • 公開(is_public)が表示(True)のもの

ビューファイルのget_querysetメソッドに検索条件を記述することで反映されます。

posts/views.py

class IndexView(generic.ListView):
    def get_queryset(self):
        return (
            Post.objects
                .filter(is_public=True)
                .order_by('-published_at')[:10]
        )

再度ブラウザで確認すると条件通りに表示されたと思います。

ページネーション(ページ分割)

一覧を10件指定して表示しましたが、過去の投稿も見れるようにしたいですね。
1ページに表示する数は抑えつつ10件以上表示する方法としてはページネーション機能を使用してページ分割するのが一般的です。

views.pyを下記のように編集します。
paginate_byで一ページに表示する数を指定して、get_querysetで指定していた件数指定は削除しましょう。

posts/views.py

class IndexView(generic.ListView):
paginate_by = 10

def get_queryset(self):
    return (
        Post.objects
            .filter(is_public=True)
            .order_by('-published_at')
    )

post_list.htmlにページナビゲーションを表示するように追記します。

posts/templates/posts/post_list.html

{% if is_paginated %}
    {% if page_obj.has_previous %}
        <a href="?page=1">
            &laquo;
        </a>
        <a href="?page={{ page_obj.previous_page_number }}">
            &lsaquo;
        </a>
    {% endif %}

    {% for number in page_obj.paginator.page_range %}
        {% if page_obj.number == number %}
            {{ number }}
        {% else %}
            <a href="?page={{ number }}">{{ number }}</a>
        {% endif %}
    {% endfor %}

    {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">
            &rsaquo;
        </a>
        <a href="?page={{ page_obj.paginator.num_pages }}">
            &raquo;
        </a>
    {% endif %}
{% endif %}

静的ファイルの表示

CSSファイルを例に静的ファイルを表示してみます。
最初に使用するCSSファイルを適当に作り配置します。

static/css/style.css

h1 {
    color: red;
}

settings.pySTATICFILES_DIRSという項目を追記して静的ファイルを配置するディレクトリを指定します。
今回はstaticですね。

django_simple_cms/settings.py

STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

レイアウトHTMLでload staticを追記して配置したCSSを読み込みましょう。

posts/templates/base.html

{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    ...
    <link rel="stylesheet" href="{% static 'css/style.css' %}">

ブラウザで表示してh1が赤色になっていることを確認してください。

詳細ページの作成

次に一覧からリンクする詳細ページを作成しましょう。
ビューにgeneric.DetailViewを継承したDetailViewクラスを作成します。

posts/views.py

# ...

class DetailView(generic.DetailView):
    model = Post

詳細ページのテンプレートはデフォルトでpost_detail.htmlが読み込まれます。
対応したデータも自動的に取得されpost変数に代入されますので、この変数を展開しましょう。

posts/templates/posts/post_detail.html

{% extends 'base.html' %}

{% block main %} 
<h1>{{ post.title }}</h1>
<p>公開日:{{ post.published_at|date:'Y年m月d日' }}</p>
<div>{{ post.body }}</div>
{% endblock %}

詳細ページにアクセスできるようにルーティングの設定を行います。

posts/urls.py

app_name = 'posts'
        
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
]

int:pk指定しているのでプライマリキー(id)でアクセスした際詳細ページが表示されるようになります。
app_nameを指定しておくとのちにアプリケーションを追加した時に区別がしやすくなります。
ブラウザでhttp://localhost:8000/posts/1にアクセスして詳細ページが表示されるか確認してみましょう。

非公開投稿にアクセスできないようにする

詳細ページを表示することができましたが、今の状態だとすべての投稿が表示されてしまいますね。
非公開にした投稿ページにアクセスしたときは404ページになるようにしましょう。
一覧と同じようにviews.pyDetailViewに検索条件を追加します。

posts/views.py

# ...

class DetailView(generic.DetailView):
    def get_queryset(self):
        return Post.objects.filter(is_public=True)

これで、非公開(is_public=False)にしたページにアクセスすると404ページが表示されるようになります。

一覧からリンク設定

先ほど作成した一覧ページからリンクを設定して遷移できるようにしてみましょう。
post_list.htmlのリスト部分を下記のように編集します。

posts/templates/posts/post_list.html

<li><a href="{% url 'posts:detail' post.id %}">{{ post.title }}</a></li>

posts:detailの部分はurls.pyで設定したapp_namenameを:で繋いだ文字列です。

これで一覧から詳細ページへリンクすることができました。

すごく簡単にでしたが、データベースの作成から管理画面からの登録、フロントの表示まで行うことができました。
Djangoはもう少しやっていこうと思いますのでまた!!

ソースコードはGitHubにのせてます。

下記サイトを参考にさせて頂きました。