EY-Office ブログ

AIのアシストしてもらいPython、Djangoを学びました

私は長年Ruby言語やRuby on Railsを使っていますが、実はPython言語やWebフレームワークDjangoを使った事がありませんでした。少し時間が出来たのでPython,Djangoを学んでみる事にしました、せっかくなのでAIの助けを借りて学んだらどうなるのかを体験してみました。

with the help of AI Gemini AI image generatorが生成した画像を使っています

Pythonの学習プランを考えてもらう

今回も無料で使えるperplexityを使いました。まずはPythonの学習プランを考えてもらいました。プロンプトは以下です、

Pythonを学びたいと思います、学習プランを立ててください。 
- 演習を行うスタイルがよいです
- 私はRuby, JavaScriptは上級者レベルです

回答は以下のような感じでした。全文はここかで読めます。

Python学習プラン(Ruby・JavaScript上級者向け、演習重視)

学習方針と全体像

  • あなたはすでにRubyとJavaScriptの上級者なので、プログラミングの基本概念やオブジェクト指向、関数型の考え方は十分理解しています。
  • Pythonの文法や「Pythonらしい書き方(Pythonic)」、標準ライブラリ、主要なエコシステムにフォーカスし、演習中心で進めましょう。
  • 実践的な演習サイトや問題集を活用し、手を動かしながら習得します。

ステップ別学習プラン

  • ステップ1:Pythonの基本文法と特徴を押さえる(1〜2日)
    • 公式チュートリアルや「Learn X in Y Minutes」のPython版で、Ruby/JSとの違いをざっと確認。
    • Pythonのインデントによるブロック、動的型付け、リスト内包表記、イテレータ・ジェネレータ、with文、デコレータ、例外処理などを重点的に。
    • おすすめリソース
  • ステップ2:基礎演習で文法・標準ライブラリを体得(3〜5日)
    • w3resourceやPYnativeなどの演習サイトで、基礎から応用まで幅広く問題を解く。
    • 変数、リスト・辞書・セット、制御構文、関数、ファイル操作、例外処理、クラス・継承など、各トピックごとに演習。
    • おすすめリソース

ステップ3以降は省略・・・

補足アドバイス

  • Ruby/JS経験者は、Python特有の「明示的」「シンプル」「美しい」設計思想(Zen of Python: import this)を意識すると、よりスムーズに馴染めます。
  • 演習は「写経」ではなく、必ず自分で手を動かし、エラーや挙動の違いを体感しながら進めてください。
  • 疑問点は公式ドキュメントやStack Overflowで調べる癖をつけましょう。

Pythonの学習と感想

まず、おすすめリソースにあったLearn X in Y Minutes: Python(高速で概要を把握)を真面目に読みました。ここにはPythonのデータ、文法などの例が大量に書かれています。 Pythonの文法などはRubyは大きく違いますがコンセプトは似ているので、PythonとRubyの違いを通じてPythonのエッセンスを知る事ができました。

次にw3resource Python Exercises(大量の演習問題と解説)Exercises for Intermediate developersを解いてみました。これによりPythonの小さなコードがだいぶ書けるようになりました。とくに以前にニューラルネットワークを学んだときには理解できなかったリスト内包表記が理解できした。成果は以下です、ネストした配列の要素の値がoldならnewに置き換えるコードです。

def replace_array(array, old, new):
  return [replace_array(x, old, new) \
    if type(x) == list else new if x == old else x for x in array]

感想

  • Rubyほどオブジェクト指向が強くない、組み込み関数がたくさんある
  • Array, Dictonary以外にSets, Tuplesのリテラルが書ける
  • 0 is FalseがWarningになるのは親切か❓
  • map関数の戻り値がmap object❗配列にするにはlist(map(...))とする
  • 配列の値を結合して文字列を作るメソッドが配列ではなく、文字列にある❗ 例: " ".join([1,2,3])

それから、最新のPythonをインストールしようとしてハマりました。以前にニューラルネットワークを学んだときにAnacondaをインストールしてあったのが原因だったようです。AIに聞いてみたところHomebrew+pyenvかPythonが持っている仮想環境venvがお薦めということでvenvを使う事にしました。venvの使い方もAIにききました。😃

Djyangoの学習プランを考えてもらう

プロンプトは、Djangoの学習プランを考えてください、Ruby on Railsは良く知っています。で、回答は以下のようになりました。

回答は以下のような感じでした。全文はここかで読めます。

Django学習プラン(Ruby on Rails経験者向け)

Ruby on Railsをよく知っている方がDjangoを効率的に学ぶための学習プランを提案します。Railsの知識を活かしつつ、Django特有の設計思想やPythonの文法に着目して進めることで、学習コストを抑えつつスムーズに習得できます。

1. Pythonの基礎文法を押さえる

  • Rails経験者であればオブジェクト指向やMVCの概念は理解済みなので、Pythonの構文(インデント、クラス、関数、リスト・辞書など)に集中しましょう。
  • RailsでRubyの直感的な文法に慣れている場合、Pythonのシンプルな文法はすぐに馴染めます。

2. Djangoの全体構造と設計思想の理解

  • Djangoは「MVT(Model-View-Template)」の設計思想を持ちます。RailsのMVCと似ていますが、Template(テンプレート)がViewの役割を担う点が異なります。
  • Railsの「規約より設定(CoC)」に対し、Djangoは「設定より規約(設定重視)」の傾向が強いです。
  • 公式チュートリアルや日本語解説書籍で、プロジェクトとアプリケーションの関係、設定ファイル、URLルーティングの仕組みを把握しましょう。

3. CRUDアプリの実装で基本機能を習得

  • RailsでのCRUDアプリ開発経験を活かし、Djangoでも同様のアプリ(例:ブログ、ToDoリスト)を作成します。
  • モデル定義(Django ORM)、マイグレーション、ビュー、テンプレート、フォーム、管理画面(admin)の使い方を一通り実践しましょう。

4. Djangoならではの機能・拡張の習得

  • Djangoの強力な管理画面(admin)、認証・認可、フォームバリデーション、カスタムミドルウェア、シグナルなど、Railsにはない特徴的な機能を重点的に学びます。
  • サードパーティパッケージ(Django REST frameworkなど)の導入や、独自アプリの分割・拡張方法も押さえましょう。

5. 実践的なWebアプリ開発・ポートフォリオ作成

  • これまで学んだ内容を活かして、実際にWebアプリを1〜2本作成します。
  • Railsで作ったことのあるアプリをDjangoで再現してみると、両者の違いが明確になり、理解が深まります。

おすすめ教材

  • 公式チュートリアル(Django公式ドキュメント)
  • 日本語の入門書・解説書(効率的な学習方法も参考に)
  • Django REST frameworkの公式ガイド(API開発も視野に)

Djangoの学習と感想

公式チュートリアルが在ったので、やってみました。Ruby on Railsとはかなり違いますが、やはりWebフレームワークなのでコードを見ながら理解が進みました。

感想

  • Railsの様なCoC(Convention over Configuration)は無く、たくさんコードを書かないと行けない
  • TemprateはRailのERBと違い、独自テンプレート言語を覚えないといけない
  • たくさんコードを書く必要がありますが、CoCの謎ルールを知らなくてもコードが追っていけるのは良いかも
  • Rails同様に、MVCアーキテクチャ、ルーティング、ORM、マイグレーション・・・など共通のコンセプトを採用していて理解しやすいです

いつものジャンケンアプリを作ってみました

いつものジャンケンアプリを作ってみました、チュートリアル以外で調べたのは、Tailwind CSSを使い方でした。

Tailwind CSSを使うにためにdjango-tailwindライブラリーをインストールしました。

ディレクトリー・ファイル

今回作成したアプリのディレクトリー・ファイル構造は以下のようになっています(__pycache__, node_modules は省略しました)。

├── db.sqlite3
├── jyanken
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── libs.py
│   ├── migrations
│   │   ├── __init__.py
│   │   ├── 0001_initial.py
│   │   └── 0002_rename_jyanken_score.py
│   ├── models.py
│   ├── templates
│   │   ├── base.html
│   │   ├── components
│   │   │   ├── application_bar.html
│   │   │   ├── jyanken_box.html
│   │   │   ├── score_list.html
│   │   │   └── status_box.html
│   │   └── jyanken
│   │       ├── scores.html
│   │       └── status.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── site_common
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── theme
    ├── __init__.py
    ├── apps.py
    ├── static
    │   └── css
    │       └── dist
    ├── static_src
    │   ├── package-lock.json
    │   ├── package.json
    │   ├── postcss.config.js
    │   └── src
    │       └── styles.css
    └── templates
        └── base.html
  • jyanken : ジャンケンアプリ
  • site_common : django-adminで作ったたサイト
  • theme : django-tailwindが作ったアプリ(設定やnpmライブラリーなど)
コード

コードに一部を載せます。

models.py

モデルです、テーブル定義と、インスタントの値を文字列で戻すプロパティを定義しています。

from django.db import models

class Score(models.Model):
  TE_STRING = ["グー", "チョキ", "パー"]
  JUDGMENT_STRING = ["引き分け", "勝ち", "負け"]
  JUDGMENT_COLOR = ["text-[#000]", "text-[#2979ff]", "text-[#ff1744]"]

  human = models.IntegerField()
  computer = models.IntegerField()
  judge = models.IntegerField()
  matchDate = models.DateTimeField(auto_now_add=True)

  @property
  def human_str(self):
    return self.TE_STRING[self.human]

  @property
  def computer_str(self):
    return self.TE_STRING[self.computer]

  @property
  def judge_str(self):
    return self.JUDGMENT_STRING[self.judge]

  @property
  def judge_color(self):
    return self.JUDGMENT_COLOR[self.judge]

  def __str__(self):
    return f'{self.human} {self.computer} {self.judge} {self.matchDate}'
libs.py

ジャンケンで使う定数、スタティック・メソッドを含むジャンケン・クラスです。

import random
from .models import Score

class Jyanken:
  GUU = 0
  CHOKI = 1
  PAA = 2

  DRAW = 0
  WIN = 1
  LOSE = 2

  @staticmethod
  def pon(human):
    score = Score()
    score.human = human
    score.computer = random.randrange(3)
    score.judge = (score.computer - score.human) % 3
    score.save()

  @staticmethod
  def get_scores():
    return Score.objects.order_by("-id")[:10]

  @staticmethod
  def get_status():
    scores = Score.objects.all()

    def judge_count(judge):
      return sum(score.judge == judge for score in scores)

    return {
      'draw': judge_count(Jyanken.DRAW),
      'win': judge_count(Jyanken.WIN),
      'lose': judge_count(Jyanken.LOSE)
    }
urls.py

このアプリのルーティングの定義です。

パス機能name
//scores/へリダイレクトindex
/scores/ジャンケン・対戦結果scores
/status/ジャンケン・対戦成績status
from django.urls import path
from . import views

urlpatterns = [
  path("", views.index, name="index"),
  path("scores/", views.scores, name="scores"),
  path("status/", views.status, name="status"),
]
views.py

Railsで言うとコントローラーでしょうか。

  • /でアクセスされるとこのindexメソッドが動き、name="scores"のパスにリダイレクトします
  • /scores/にPOSTアクセスがあるとジャンケンを実行し、同じパスにリダイレクト(GET)します
    • request.POST['hand']でPOSTされた値を受け取ります
  • /scores/にGETアクセスがあると、jyanken/scores.htmlテンプレートが表示されます
    • テンプレートで参照される値は、contextで渡します
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .libs import Jyanken


def index(request):                                       # ← ①
  return HttpResponseRedirect(reverse("scores"))

def scores(request):
  if request.method == 'POST':                            # ← ②
    Jyanken.pon(int(request.POST['hand']))
    return HttpResponseRedirect(request.path_info)
  else:                                                   # ← ③
    scores = Jyanken.get_scores()
    context = {"scores": scores, "Jyanken": Jyanken}
    return render(request, "jyanken/scores.html", context)

def status(request):
  if request.method == 'POST':
    Jyanken.pon(int(request.POST['hand']))
    return HttpResponseRedirect(request.path_info)
  else:
    status = Jyanken.get_status()
    context = {"status": status, "Jyanken": Jyanken}
    return render(request, "jyanken/status.html", context)
templates/base.html

レイアウト用テンプレートです。

  • 全てのテンプレートはこのテンプレートを継承して<head><body>タグを表示します
  • Tailwind CSSを組み込んでいます
{% load static tailwind_tags %}

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{% block title %}{% endblock %}</title>
    {% tailwind_css %}
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
templates/jyanken/scores.html

ジャンケンのテンプレートで、ジャンケン・ボタンと対戦結果を表示します。

  • {% block content %}{% endblock %}の内容が、base.htmlの{% block content %}{% endblock %}に取り込まれます
  • {% include ... %}は他のテンプレートの取り込みです
{% extends 'base.html' %}

{% block title %}対戦結果{% endblock %}

{% block content %}

{% include "components/application_bar.html" %}
<div class="mx-2 md:mx-8 md:w-1/2">
  <h1 class="my-6 text-center text-xl font-bold">対戦結果</h1>
  {% include "components/jyanken_box.html" %}
  {% include "components/score_list.html" %}
</div>

{% endblock %}
templates/components/application_bar.html

アプリケーションバー表示テンプレートです。

  • {% with }はテンプレート内で使える変数です
{% with link_class="py-2 px-3 text-blue-100 rounded hover:bg-blue-700" %}

<nav class="bg-blue-600 border-gray-50">
  <div class="max-w-screen-xl flex flex-wrap items-center mx-auto p-3">
    <h1 class="ml-5 text-2xl font-bold text-white">じゃんけん ポン!</h1>
      <ul class="font-medium flex p-2 bg-blue-600">
        <li>
          <a href="/scores" class="{{link_class}}">対戦結果</a>
        </li>
        <li>
          <a href="/status" class="{{link_class}}">対戦成績</a>
        </li>
      </ul>
  </div>
</nav>

{% endwith %}
templates/components/jyanken_box.html

グー・チョキ・パーボタンの並ぶテンプレートです、ボタン毎にFormになっています。

{% with button_class="text-white text-center text-sm rounded w-16 px-2 py-2 bg-blue-600 hover:bg-blue-700 shadow shadow-gray-800/50" %}

<div class="w-[230px] mx-auto flex mb-10">
  <form method="post">
    {% csrf_token %}
    <button type="submit" class="{{button_class}}">グー</button>
    <input type="hidden" name="hand" value="{{Jyanken.GUU}}" />
  </form>
  <form method="post">
    {% csrf_token %}
    <button type="submit" class="{{button_class}} mx-5">チョキ</button>
    <input type="hidden" name="hand" value="{{Jyanken.CHOKI}}" />
  </form>
  <form method="post">
    {% csrf_token %}
    <button type="submit" class="{{button_class}}">パー</button>
    <input type="hidden" name="hand" value="{{Jyanken.PAA}}" />
  </form>
</div>

{% endwith %}
templates/components/score_list.html

対戦結果を表示するテンプレートです


<table class="w-full text-sm text-left text-gray-500">
  <thead class="bg-slate-100 border">
    <tr>
      <th class="px-6 py-3">時間</th>
      <th class="px-6 py-3">人間</th>
      <th class="px-6 py-3">コンピュータ</th>
      <th class="px-6 py-3">結果</th>
    </tr>
  </thead>
  <tbody class="bg-white border">
  {% for score in scores %}
    <tr class="bg-white border-b">
      <td class="px-2 md:px-6 py-4 {{score.judge_color}}">{{score.matchDate|date:"H:i:s"}}</td>
      <td class="px-2 md:px-6 py-4 {{score.judge_color}}">{{score.human_str}}</td>
      <td class="px-2 md:px-6 py-4 {{score.judge_color}}">{{score.computer_str}}</td>
      <td class="px-2 md:px-6 py-4 {{score.judge_color}}">{{score.judge_str}}</td>
    </tr>
  {% endfor %}
  </tbody>
</table>

まとめ

今回、AIにアシストしてもらいながらPython言語とDjangoフレームワークを学びました。従来の本やネット上の情報をベースに学習するのに比べて効率が良かったと思います、感覚的な数値ですが1.5〜2倍くらい短い時間で終わったきがします。

ただし、

  • 私は長年ソフトウェア開発を行っている
  • Pythonに似ているRubyを熟知している
  • Djangoに似ているRuby on Railsを熟知している

という前提があります。

AIは質問に対して大抵、1つの答えを示してくれますが、それが必ずしも最適ではないこともありました。DjangoでTailwind CSSを使う方法へのAIの回答は、独立にnpmライブラリーを使う方法でしたが、私は何か良いライブラリーが在るに違いないと思い、Google検索し django-tailwindライブラリーを発見しました。
長年ソフトウェア開発を行っている人には直感的に気が付くかもしれませんが、初心者には難しいかも知れませんね。

このようにAIの回答が最適ではない事があります、また情報が古かったり、ハルシネーションも起きるかもしれません。そもそも、初心者は適切な質問(プロンプト)が書けない可能性も高いと思います。
私は教育も行っていますが、ソフトウェア開発の初心者には、まだ人間のアシストの方が良いと思いました。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ