🔧

【Django】と「DRY原則」について【超初心者向け】

2025/01/25に公開

DIYは割と好きな方です。
こんにちは、
ワニかず@40歳 出戻りエンジニアです。

さて、今回はDjango(ジャンゴ)と
DRY原則(Don't Repeat Yourself)について、
具体例を混ぜてまとめてみました。

Djangoとは?

Djangoは、Pythonで書かれた強力なWebアプリケーションフレームワークです。その主な特徴は、

  1. MVTアーキテクチャ
  • Model(データベース構造の定義)
  • View(ロジックの処理)
  • Template(画面表示)
    という構造で開発を進められます
  1. 管理画面が標準装備
    データベースの内容を簡単に確認・編集できる管理画面が自動生成されます

  2. セキュリティ対策:
    SQLインジェクションやクロスサイトスクリプティングなどに対する保護機能が標準で組み込まれています

  3. ORM(Object-Relational Mapping):
    SQLを直接書かなくても、Pythonのコードでデータベース操作ができます

  4. 豊富なライブラリ
    認証、フォーム処理、ファイルアップロードなど、よく使う機能が標準で用意されています

InstagramやMozillaなどの大規模サービスでも採用されており、信頼性の高いフレームワークとして知られています。

DRY原則とは?

DRY原則の本質は「同じ知識や情報をシステム内で重複して持たないようにする」という考え方です。
主なポイントは以下の通りです。

  1. コードの重複を避ける
  • 同じような処理を複数の場所で書かない
  • 共通の処理は関数やクラスにまとめる
  • ユーティリティ関数を作成して再利用する
    →後で良い例、悪い例をあげて説明します。
  1. メリット
  • コードの保守性が向上する
  • バグ修正が1箇所で済む
  • コードの一貫性が保たれる
  • テストがしやすくなる
  1. 適用例
BAD例(重複あり)
def validate_user_name(name):
    if len(name) < 3:
        raise ValueError("名前は3文字以上必要です")
    if len(name) > 20:
        raise ValueError("名前は20文字以下にしてください")

def validate_product_name(name):
    if len(name) < 3:
        raise ValueError("名前は3文字以上必要です")
    if len(name) > 20:
        raise ValueError("名前は20文字以下にしてください")
GOOD例(DRY原則に従った実装)
def validate_name_length(name, field_name="名前"):
    if len(name) < 3:
        raise ValueError(f"{field_name}は3文字以上必要です")
    if len(name) > 20:
        raise ValueError(f"{field_name}は20文字以下にしてください")
  1. 注意点
  • 過度な抽象化は避ける
  • コードの重複と知識の重複は異なる
  • 時にはコードの重複が適切な場合もある
  1. データベース設計での応用
  • 正規化によってデータの重複を排除
  • 参照整合性の維持
  • マスターデータの一元管理

プログラミングにおいて重要な原則の一つであり、
保守性の高い良質なコードを書くための指針となります。

DjangoでのDRY原則

以下のようにbase.htmlと
個別のテンプレートに分ける、という構造があります。

実際の例(良い例)

templates/
├── base.html          # 共通レイアウト
└── pages/
    ├── calendar.html  # カレンダーページ
    ├── events.html    # イベント一覧ページ
    └── profile.html   # プロフィールページ

これらすべてのページで

同じヘッダー
同じナビゲーション
同じフッター
同じCSSフレームワーク

を使用したい場合、base.htmlに共通部分を定義することで効率的に管理できます。
このアプローチは多くのWebフレームワーク(Laravel、Ruby on Rails、Flaskなど)でも採用されている標準的なパターンです。

base.html
# base.html
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
    <!-- 共通のCSSやJSはここに -->
</head>
<body>
    <nav><!-- 共通のナビゲーション --></nav>
    
    {% block content %}{% endblock %}
    
    <footer><!-- 共通のフッター --></footer>
</body>
</html>
calendar.html
# calendar.html
{% extends 'base.html' %}

{% block title %}カレンダー{% endblock %}

{% block content %}
    <!-- カレンダー固有のコンテンツ -->
{% endblock %}

1. 「同じような処理を複数の場所で書かない」の実践

base.htmlに共通のHTML構造(head, body, nav, footer)を1回だけ定義しています
これにより、各ページで同じHTMLの骨格を繰り返し書く必要がありません
例えば、calendar.htmlでは共通部分を再定義せず、固有のコンテンツのみを定義しています

2. 「共通の処理は関数やクラスにまとめる」の実践

  • base.html自体が「基本テンプレート」というクラスのような役割を果たしています
  • 共通の構造やレイアウトをbase.htmlにまとめることで、一元管理を実現しています
  • {% block %}タグを使用して、カスタマイズ可能な部分を明確に定義しています

3. 「ユーティリティ関数を作成して再利用する」の実践

  • {% extends 'base.html' %}によって、基本テンプレートの再利用を実現しています
  • {% block title %}{% endblock %}や{% block content %}{% endblock %}は、再利用可能なブロックとして機能します
  • これらのブロックは、異なるページで異なるコンテンツを挿入できる「ユーティリティ」として働きます

4. 実際の効果

例えば10ページのウェブサイトがある場合...

DRYを使用しない場合:
- 各ページで共通のヘッダー、ナビゲーション、フッターを10回記述
- HTML構造を10回記述
- 修正が必要な場合、10箇所を修正

DRYを使用する場合(現在のコード):
- 共通部分は base.html に1回だけ記述
- 各ページは extends で base.html を継承し、固有部分のみを記述
- 修正が必要な場合、base.html の1箇所を修正するだけ

このように、テンプレート継承を使用することで、コードの重複を避け、メンテナンス性を高めています。

悪い例(DRY原則を実践していない)

# page1.html
<!DOCTYPE html>
<html>
<head>
    <title>ページ1</title>
    <link rel="stylesheet" href="style.css">
    <script src="common.js"></script>
</head>
<body>
    <nav>
        <ul>
            <li><a href="/">ホーム</a></li>
            <li><a href="/about">概要</a></li>
            <li><a href="/contact">お問い合わせ</a></li>
        </ul>
    </nav>
    
    <div class="content">
        <h1>ページ1のコンテンツ</h1>
        <!-- ページ1固有のコンテンツ -->
    </div>
    
    <footer>
        <p>&copy; 2025 My Company</p>
        <ul>
            <li><a href="/privacy">プライバシーポリシー</a></li>
            <li><a href="/terms">利用規約</a></li>
        </ul>
    </footer>
</body>
</html>
# page2.html
<!DOCTYPE html>
<html>
<head>
    <title>ページ2</title>
    <link rel="stylesheet" href="style.css">
    <script src="common.js"></script>
</head>
<body>
    <nav>
        <ul>
            <li><a href="/">ホーム</a></li>
            <li><a href="/about">概要</a></li>
            <li><a href="/contact">お問い合わせ</a></li>
        </ul>
    </nav>
    
    <div class="content">
        <h1>ページ2のコンテンツ</h1>
        <!-- ページ2固有のコンテンツ -->
    </div>
    
    <footer>
        <p>&copy; 2025 My Company</p>
        <ul>
            <li><a href="/privacy">プライバシーポリシー</a></li>
            <li><a href="/terms">利用規約</a></li>
        </ul>
    </footer>
</body>
</html>

この例の問題点

  1. 「同じような処理を複数の場所で書かない」の違反
  • ナビゲーションメニューが両方のファイルで完全に重複している
  • フッターの内容が両方のファイルで重複している
  • head要素内のCSSとJavaScriptの読み込みが重複している
# page1.html と page2.html の両方で重複している部分:

<!-- ナビゲーションの重複 -->
<nav>
    <ul>
        <li><a href="/">ホーム</a></li>
        <li><a href="/about">概要</a></li>
        <li><a href="/contact">お問い合わせ</a></li>
    </ul>
</nav>

<!-- フッターの重複 -->
<footer>
    <p>&copy; 2025 My Company</p>
    <ul>
        <li><a href="/privacy">プライバシーポリシー</a></li>
        <li><a href="/terms">利用規約</a></li>
    </ul>
</footer>

<!-- head要素内の重複 -->
<head>
    <link rel="stylesheet" href="style.css">
    <script src="common.js"></script>
</head>
  1. 「共通の処理は関数やクラスにまとめる」の違反
  • 共通のレイアウト構造が各ファイルで独立して定義されている
  • テンプレート継承を使用していないため、共通部分をまとめていない
# 両ファイルで独立して定義されている共通構造
<!DOCTYPE html>
<html>
<head>
    <!-- head内容 -->
</head>
<body>
    <nav>
        <!-- nav内容 -->
    </nav>
    <div class="content">
        <!-- コンテンツ -->
    </div>
    <footer>
        <!-- footer内容 -->
    </footer>
</body>
</html>
  1. 「ユーティリティ関数を作成して再利用する」の違反
  • 共通のコンポーネント(nav, footer)が再利用可能な形で定義されていない
  • 各ページで同じHTMLを繰り返し記述している
# 再利用可能なコンポーネントとして分離されるべき部分
<nav>
    <ul>
        <li><a href="/">ホーム</a></li>
        <li><a href="/about">概要</a></li>
        <li><a href="/contact">お問い合わせ</a></li>
    </ul>
</nav>

<footer>
    <p>&copy; 2025 My Company</p>
    <ul>
        <li><a href="/privacy">プライバシーポリシー</a></li>
        <li><a href="/terms">利用規約</a></li>
    </ul>
</footer>

テンプレート継承の歴史

テンプレート継承(base.htmlとその他のテンプレートという構造)という形となった歴史が気になったので調べました。

  1. 1990年代後半〜2000年代初頭の課題
  • 初期のWebサイトではHTMLファイルごとにヘッダーやフッターをコピー&ペーストしていた
  • サイト全体のデザイン変更時に、すべてのファイルを手動で更新する必要があった
  • これは非効率で、ミスが発生しやすかった
  1. PHPの登場と影響(1995年〜)
  • PHPはinclude機能を導入
  • ヘッダーやフッターを別ファイルとして分離し、再利用できるようになった
php
<?php include 'header.php'; ?>
コンテンツ
<?php include 'footer.php'; ?>
  1. テンプレートエンジンの発展(2000年代)
  • Smarty(PHP, 2002年)
  • Ruby on Railsのテンプレートシステム(2004年)
  • Django(2005年)これらが「テンプレート継承」という概念を確立
  1. Djangoの貢献
  • Djangoは新聞社のCMSとして開発され、効率的なコンテンツ管理が必要だった
  • extendsとblockの仕組みを導入し、より柔軟なテンプレート継承を実現
  • この設計パターンは他のフレームワークにも影響を与えた
  1. 現代の発展
  • コンポーネントベースの開発(React, Vue.js)
  • マイクロフロントエンド
  • しかし、テンプレート継承の基本的な考え方は今でも有効

このような進化は、以下の開発原則に基づいています

  • DRY(Don't Repeat Yourself)
  • 関心の分離(Separation of Concerns)
  • 保守性の向上
  • 開発効率の改善

このように、Webの発展とともにコードの再利用性と保守性を高めるためにテンプレート継承のような構造が生まれ、現在でも広く使用されています。

Discussion