【初心者OK】Django + Cloud ShellでECサイトの基礎!商品カタログWebアプリ開発

この記事では、Python/Djangoを使って商品カタログWebアプリの作成方法を解説します。商品一覧ページと商品詳細ページを持つシンプルなアプリケーションを構築していきます。

Djangoの基本については、以下の記事をご参照ください!

開発環境の準備

まずは、必要なツールをインストールしましょう。

GCPのCloudShellを利用することを想定しています。

# Pythonがインストールされていることを前提としています
# Djangoのインストール
pip install django

# プロジェクトの作成
django-admin startproject catalog_project
cd catalog_project

# アプリケーションの作成
python manage.py startapp products

# 仮想環境の作成
python -m venv venv

# 仮想環境の有効化(Windows)
venv\Scripts\activate

# 仮想環境の有効化(Mac/Linux)
source venv/bin/activate

Pillowのインストールを行う

Djangoの ImageField (この後利用します)は、画像のアップロードや操作(サムネイルの作成など)を行う際に、Pillow ライブラリに依存しています。

当アプリには必要なライブラリですので、インストールしておきましょう。

python -m pip install Pillow

もし、インストールせずに先に進もうとすると、モデル作成後のマイグレーションでエラーになります。

ERRORS:
products.Product.image: (fields.E210) Cannot use ImageField because Pillow is not installed.
        HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".

モデルの作成

products/models.py ファイルを編集して、商品情報を格納するモデルを作成します。

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    image = models.ImageField(upload_to='products/', null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.name

# Create your models here.がデフォルトで記載されていますが、削除しても問題ありません。

以下のコードを記載すると、Djangoの管理画面に項目名が出力されるようになります。

    def __str__(self):
        return self.name

プロジェクト設定の更新

catalog_project/settings.py ファイルを編集して、作成したアプリケーションを登録します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products',  # 追加
]

# メディアファイル設定(商品画像用)
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

URLの設定

注意点
1. プロジェクトとアプリケーションの [ urls.py ] ファイルを混同しないように注意しましょう。
2. 修正対象が何か?を把握することが大切です。

catalog_project/urls.py ファイルを編集しましょう。

まず、プロジェクトレベルのURLを設定します。

修正前

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

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

修正後

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

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

# メディアファイル配信用の設定
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

products/urls.py ファイルを作成・修正

次に、アプリケーションレベルのURLを設定します。

まず、products/ フォルダの配下に urls.py  ファイルを作成します。やり方は2種類ありますが、どちらでやっても構いません。

  1. productsフォルダに移動し、Linuxコマンドを実行する。
  2. catalog_projectフォルダのurls.pyファイルをコピーして、productsフォルダにペーストする。

せっかく勉強するので、私はLinuxコマンドの方をお勧めします。

# アプリケーションフォルダ(catalog_project/products)にいることを確認しましょう。
# pwdは、自分のフォルダの位置を確認するコマンドです。

(venv) Rokuta@Terminal %1~ %# pwd
/home/yourdirectory/catalog_project/products

# アプリケーションフォルダにいない場合は、以下のコマンドで移動します。

cd <移動するフォルダ>

# touchコマンドで、ファイルを作成します。

(venv) Rokuta@Terminal %1~ %# touch urls.py 
from django.urls import path
from . import views

app_name = 'products'

urlpatterns = [
    path('', views.product_list, name='product_list'),
    path('product/<int:product_id>/', views.product_detail, name='product_detail'),
]

ビューの作成

products/views.py ファイルを編集して、ビュー関数を定義します。

from django.shortcuts import render, get_object_or_404
from .models import Product

def product_list(request):
    products = Product.objects.all()
    return render(request, 'products/index.html', {'products': products})

def product_detail(request, product_id):
    product = get_object_or_404(Product, pk=product_id)
    return render(request, 'products/detail.html', {'product': product})

テンプレートの作成

1. テンプレートフォルダの作成

テンプレートフォルダを作成します。

このフォルダの中に、複数のHTML(テンプレート)を作成して格納します。

mkdir -p products/templates/products

2. index.htmlファイルの作成

products/templates/products/index.html ファイルを作成しましょう。

このテンプレートは、商品の一覧を表示するためのファイルです。

作成したHTMLファイルに、以下のように記述します。

{% extends 'base.html' %}

{% block content %}
<h1>商品一覧</h1>
<div class="product-grid">
    {% for product in products %}
    <div class="product-card">
        <a href="{% url 'products:product_detail' product.id %}">
            {% if product.image %}
            <img src="{{ product.image.url }}" alt="{{ product.name }}">
            {% else %}
            <div class="no-image">No Image</div>
            {% endif %}
            <h2>{{ product.name }}</h2>
            <p class="price">¥{{ product.price }}</p>
        </a>
    </div>
    {% empty %}
    <p>商品がありません。</p>
    {% endfor %}
</div>
{% endblock %}

3. detail.htmlファイルの作成

HTMLファイルを、もう一つ作成しましょう。

products/templates/products/detail.html ファイルを作成します。

{% extends 'base.html' %}

{% block content %}
<div class="product-detail">
    <div class="back-link">
        <a href="{% url 'products:product_list' %}">← 商品一覧に戻る</a>
    </div>
    
    <div class="product-content">
        <div class="product-image">
            {% if product.image %}
            <img src="{{ product.image.url }}" alt="{{ product.name }}">
            {% else %}
            <div class="no-image">No Image</div>
            {% endif %}
        </div>
        
        <div class="product-info">
            <h1>{{ product.name }}</h1>
            <p class="price">¥{{ product.price }}</p>
            <div class="description">
                {{ product.description|linebreaks }}
            </div>
        </div>
    </div>
</div>
{% endblock %}

4. base.htmlファイルの作成

注意点
base.html ファイルは、プロジェクトのルートディレクトリ (catalog_project フォルダ) 直下に templates フォルダを作成し、その中に配置するのが一般的です。

基本テンプレート templates/base.html を作成しましょう。

mkdir -p templates

templates/base.html ファイルを作成します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>商品カタログ</title>
    <style>
        body {
            font-family: 'Helvetica Neue', Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        .product-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 30px;
        }
        
        .product-card {
            border: 1px solid #ddd;
            border-radius: 8px;
            overflow: hidden;
            transition: transform 0.3s ease;
        }
        
        .product-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 20px rgba(0,0,0,0.1);
        }
        
        .product-card a {
            text-decoration: none;
            color: inherit;
            display: block;
        }
        
        .product-card img, .no-image {
            width: 100%;
            height: 200px;
            object-fit: cover;
            display: block;
        }
        
        .no-image {
            background-color: #f5f5f5;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #999;
        }
        
        .product-card h2 {
            margin: 15px;
            font-size: 1.2rem;
        }
        
        .price {
            font-weight: bold;
            color: #e63946;
            margin: 0 15px 15px;
        }
        
        .product-detail {
            margin-top: 30px;
        }
        
        .back-link {
            margin-bottom: 20px;
        }
        
        .back-link a {
            color: #4361ee;
            text-decoration: none;
        }
        
        .product-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 40px;
        }
        
        .product-image img, .product-image .no-image {
            width: 100%;
            border-radius: 8px;
            height: 400px;
        }
        
        .product-info h1 {
            margin-top: 0;
            margin-bottom: 10px;
        }
        
        .description {
            margin-top: 20px;
            line-height: 1.8;
        }
        
        @media (max-width: 768px) {
            .product-content {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <header>
        <h1><a href="{% url 'products:product_list' %}" style="text-decoration: none; color: inherit;">商品カタログ</a></h1>
    </header>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #ddd; text-align: center;">
        <p>© 2025 商品カタログ</p>
    </footer>
</body>
</html>

(補足)各テンプレートファイルの格納について

Djangoは、複数の場所でテンプレートファイルを検索するように設定できます。

settings.py ファイルの TEMPLATES 設定で、どのディレクトリをテンプレートの検索対象とするかを指定します。この後実際にsettings.pyファイルを修正しますが、プロジェクトレベルの templates フォルダを DIRS オプションに追加することで、Djangoが base.html を見つけられるようになります。

一方、products/templates/products/index.html のように、アプリケーションごとの templates フォルダの中にさらにアプリケーション名と同じ名前のフォルダを作成するのは、テンプレートの名前空間を管理するためによく行われる慣習です。

これにより、複数のアプリケーションで同じ名前のテンプレートファイルが存在しても、Djangoがどちらのテンプレートを読み込むべきかを明確にすることができます。

フォルダ階層を全体的に図示すると、以下のようになります。

catalog_project/
├── products/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── templates/
│   │   └── products/
│   │       ├── detail.html
│   │       └── index.html
│   ├── urls.py
│   └── views.py
├── catalog_project/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── templates/
│   └── base.html  <-- ここに配置します
└── venv/
    └── ...

管理サイトの設定

products/admin.py ファイルを編集して、管理サイトから商品を追加できるようにします。

from django.contrib import admin
from .models import Product

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'price', 'created_at']
    search_fields = ['name', 'description']

プロジェクト設定の更新(templates)

catalog_project/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',
            ],
        },
    },
]

データベース設定とマイグレーション

マイグレーションファイルを作成して、データベースに反映しましょう。

python manage.py makemigrations
python manage.py migrate

管理ユーザーの作成

python manage.py createsuperuser

Username (leave blank to use '**ここに、あなたの開発環境が入ります。**'): #ユーザIDを指定しましょう
Email address: #メールアドレスを指定します。
Password: #堅牢なパスワードを指定しましょう。
Password (again): 

Superuser created successfully.

指示に従って、ユーザー名、メールアドレス、パスワードを設定します。

ここで設定したユーザ情報が、後々のログイン情報になるので忘れないようにどこかにメモしておきましょう。

開発サーバーの起動

python manage.py runserver

これで開発サーバーが起動します。ブラウザで http://127.0.0.1:8000/ にアクセスすると商品一覧ページが表示されます。

まだ商品を追加していないので何も表示されませんが、以下のようなトップ画面が表示されれば成功です!

また、http://127.0.0.1:8000/admin/ にアクセスして管理サイトにログインし、商品を追加できます。

商品データの追加方法(manage.py shell を使用した手動投入)

Django のシェル (manage.py shell) を使用して、Python コードから直接データベースにデータを追加する方法です。

Django シェルを起動

Cloud Shell のターミナルで、catalog_project フォルダに移動し、以下のコマンドを実行して Django シェルを起動します。

python manage.py shell

すると、>>> または In [1]: のようなプロンプトが表示され、Django シェルが起動します。

このプロンプトが表示された状態で、テストデータを投入する Python コードを入力し、実行します。

モデルをインポート

一行ずつ入力して Enter キーを押すか、まとめて貼り付けて実行することもできます。

from products.models import Product
from django.utils import timezone

# テストデータの作成
product1 = Product(
    name="テスト商品1",
    description="これはテスト商品1の詳細な説明です。",
    price=980.00,
    created_at=timezone.now()
)
product1.save()

product2 = Product(
    name="テスト商品2",
    description="こちらはテスト商品2についての情報です。",
    price=1200.50,
    created_at=timezone.now()
)
product2.save()

print("テストデータが投入されました。")

実行時のイメージは以下の通りです。

追加後のイメージ

一覧(index.html)

商品詳細(detail.html)

ローカル環境(GCP CloudShell以外)で実行するならば

  1. ブラウザで http://127.0.0.1:8000/admin/ にアクセス
  2. 作成した管理者アカウントでログイン
  3. 「Products」セクションの「Products」リンクをクリック
  4. 「ADD PRODUCT」ボタンをクリック
  5. 商品名、説明、価格、画像を入力/アップロード
  6. 「SAVE」ボタンをクリック

これを繰り返して、複数の商品を追加できます。追加した商品は、トップページ(http://127.0.0.1:8000/)に表示されます。

ご容赦いただきたいこと
GCP CloudShellでは、管理画面にログインしデータを投入する。ということができませんでした。
こちらをご利用の方は、ターミナルからモデルのインポートを実施してください。

まとめ

このチュートリアルでは、Djangoを使って基本的な商品カタログアプリケーションを作成しました。 商品一覧ページと商品詳細ページの基本的な機能を実装し、商品を追加・編集できるようにしました。

ここまでの経験を通じて、プログラミングによってアプリケーションを作成することを大家に他だけたのではないかと思います。

Djangoの機能を活用すれば、今後機能を比較的簡単に追加できるでしょう。