- Python 3.11.4 / Django 4.2.4
目次
チュートリアル①
簡単な投票 (poll) アプリケーションを作る!
- ユーザーが投票したり結果を表示したりできる公開用サイト
- 投票項目の追加、変更、削除を行うための管理(admin)サイト
まずDjangoがインストールされているかどうかを下記のコードで調べる。
python -m django --version
バージョンが表示されればDjanogoは正しくインストールされている!
- プロジェクトの作成 初めてDjango を使うのなら、最初のセットアップを行う必要がある。これには、自動的にプロジェクトの基本コードを生成する作業が含まれる。Djangoの「プロジェクト」とは、データベースの設定やDjangoのオプション、その他の設定を一まとめにしたものを指す。 デスクトップに新しくフォルダを作成し、コマンドプロンプトでフォルダまで移動する。
cd Desktop\\practice_dj
移動したら下のコマンドを実行する。django-admin startproject mysite
これを実行すると現在のディレクトリにmysiteディレクトリが作成される。 ※プロジェクトの名前を付けるとき組み込みの Python モジュールや Django のコンポーネントの名前を使わないように!とりわけ、 django (Django 自体と名前が衝突する) や test (組み込みの Python パッケージ名と名前が衝突する) は使ったらだめ。- mysiteディレクトリの中身
mysite/ # (外側)プロジェクトのコンテナ manage.py # プロジェクトに対する様々な操作を行うためのコマンドラインユーティリティ mysite/ # (内側)このプロジェクトの実際のPythonパッケージ # import時使用する(import mysite.urls) __init__.py # このディレクトリがPythonパッケージであることをPythonに知らせるための空のファイル settings.py # プロジェクトの設定ファイル urls.py # プロジェクトのURL宣言=目次 asgi.py # プロジェクトを提供する ASGI 互換 Web サーバーのエントリポイント wsgi.py # プロジェクトをサーブするためのWSGI互換Webサーバーとのエントリーポイント
- mysiteディレクトリの中身
- 開発用サーバー プロジェクトがうまく動作するか確認!外側のmysiteディレクトリに移動できたら下記のコマンドを実行する。
python manage.py runserver
すると下記のような出力が確認できる。Performing system checks... System check identified no issues (0 silenced). You have unapplied migrations; your app may not work properly until they are applied. Run 'python manage.py migrate' to apply them. 8月 04, 2023 - 15:50:53 Django version 4.2, using settings 'mysite.settings' Starting development server at **<http://127.0.0.1:8000/**> Quit the server with CONTROL-C. # 適用されていないデータベースマイグレーションについての警告はここでは無視。
これでDjango開発サーバを起動完了!Django開発サーバはPythonだけで書かれた軽量なWebサーバで、開発を迅速に行い運用に適した状態になるまでApacheのような運用サーバの設定をいじらなくても良いようにするためのもの。 ※このサーバは開発中の利用だけを考えて作られているので、運用環境では使ったらだめ。 ブラウザで http://127.0.0.1:8000/ にアクセスすると、The install worked successfully! Congratulations! とロケットが離陸しているページが表示される。- ポート番号の変更 デフォルトでは8000で起動する。サーバーのポートを変えたい場合は、以下のようにコマンドライン引数を渡すと、ポート8080で起動させられる。
python manage.py runserver 8080
サーバの IP を指定するときには、ポート番号も一緒に指定する。例えば、 全ての IP からのリクエストを受け付ける (サーバを他のコンピュータから見えるようにする) には、以下のようにする。python manage.py runserver 0.0.0.0:8000
- ポート番号の変更 デフォルトでは8000で起動する。サーバーのポートを変えたい場合は、以下のようにコマンドライン引数を渡すと、ポート8080で起動させられる。
- プロジェクトとアプリケーションの違い アプリとはブログシステム、公的記録のデータベース、小規模な投票アプリなど何かを行う Web アプリケーションで、プロジェクトは特定のウェブサイトの構成とアプリのコレクション。プロジェクトには複数のアプリを含めることができ、アプリは複数のプロジェクトに存在できる。
- Polls アプリを作る Django 内に追加する各アプリケーションは、所定の規約に従った Python パッケージで構成される。Django には基本的なディレクトリ構造を自動生成するユーティリティが含まれているので、ディレクトリを作ることではなくコードを書くことに集中できる。 このチュートリアルでは、 mysiteのサブモジュールではなく、それ自身のトップレベルモジュールとしてインポートできるように、 manage.pyファイルと同じディレクトリにポーリングアプリを作成する。 アプリケーションを作るには、 manage.pyと同じディレクトリに入って、このコマンドを実行する。
python manage.py startapp polls
するとpollsというディレクトリが追加される。その中身はpolls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py
このディレクトリ構造がpollアプリケーションの全体像。 - ビュー作成 最初のビューを書いてみる! polls/views.pyを開いて、以下のコードを書く。
# ユーザーが該当urlにアクセスすると"Hello, World!"というメッセージをブラウザに表示するビュー from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
ビューを呼ぶために、 URL を対応付けする必要がある。そのためにはURLconf が必要。 URLconfをつくるために、urls.pyというファイルをつくる。 コマンドプロンプトでpollsディレクトリに移動し、以下のコマンドを使用するとurls.pyがつくられる。echo. > [urls.py](<http://urls.py/>)
# 現在のpollsの中身 polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py urls.py views.py
polls/urls.pyファイルには以下のコードを書く。from django.urls import path from . import views urlpatterns = [ path("", views.index, name="index"), ]
URLconfにpolls.urlsモジュールの記述を反映させる。 mysite/urls.pyにdjango.urls.includeのimportを追加して、urlpatternsのリストにinclude()を挿入する。from django.contribimport admin from django.urlsimport include, path urlpatterns = [ path("polls/", include("polls.urls")), path("admin/", admin.site.urls), ]
include()関数を使うと、特定のURL部分を別のURL設定ファイルに委譲できる。これにより、例えばpollsアプリケーションのURL設定を独立させることができ、それを”/polls/”や”/fun_polls/”のような任意の場所に配置できる。これでアプリのURL管理が柔軟かつ簡単になる。 これでindexビューをURLconfに紐付けることができたので、下記のコマンドを実行して、動作を確認するpython manage.py runserver
ブラウザで http://localhost:8000/polls/ にアクセスすると、 “Hello, world. You’re at the polls index.” と表示されるのが確認できる。これはビューのindexで定義したもの。 ※もしここでエラーページが表示された場合は、http://localhost:8000/ではなく、http://localhost:8000/polls/移動していることを確認する。 - path関数 path() 関数は、指定されたページ、リソース、画像、添付ファイルの絶対パスを返す。引数のうちrouteとviewの2つは必須で、kwargs、nameの2つは省略可能。
- path()引数:route routeはURLパターンを含む文字列で、リクエストを処理するとき、Djangoはurlpatternsのはじめのパターンから開始し、リストを順に下に見ていく。要求されたURLと一致するものを見つけるまで各パターンと比較する。 パターンはGETやPOSTのパラメーター、そしてドメイン名を検索しない。 例えば、https://www.example.com/myapp/へのリクエストにおいては、URLconfはmyapp/を見る。https://www.example.com/myapp/?page=3へのリクエストにおいても、URLconfはmyapp/を見る。
- path()引数:view Djangoがマッチする正規表現を見つけると、指定されたビュー関数を呼び出す。その際はHttpRequestオブジェクトを第一引数に、そしてキーワード引数としてrouteから「キャプチャされた」値を呼び出す。
- path()引数:kwargs 任意のキーワード引数を辞書として対象のビューに渡せる。
- path()引数:name URLに名前付けをしておけばDjangoのどこからでも明確に参照でき、とくにテンプレートの中で有効。この便利な機能のおかげで、プロジェクトのURLにグローバルな変更を加える場合にも1つのファイルを変更するだけで済むようになる。
チュートリアル②
データベースをセットアップして、最初のモデルを作成し、そしてDjangoが自動的に生成してくれる管理サイトについての簡単なイントロダクション!
- Databaseの設定 mysite/settings.pyを開く。 Pythonに標準で組み込まれており、データベースをサポートするために何も追加でインストールする必要がないため、SQLite(オープンソースで軽量のRDBMS(データベース管理システム))を使用する。 ※ただし本番の環境で使う場合には、頭痛の種となるデータベースの移行作業を避けるため、PostgreSQL などのよりスケーラブルなデータベースを使った方が良い。 mysite/settings.pyを編集する際、TIME_ZONEに自分のタイムゾーンも設定する。(JST) 同じくファイル先頭にある INSTALLED_APPS はこのDjangoインスタンスの中で有効化されているすべてのDjangoアプリケーションの名前を保持している。デフォルトでは以下のアプリケーションが入っている。
- django.contrib.admin – 管理(admin)サイト
- django.contrib.auth – 認証システム
- django.contrib.contenttypes – コンテンツタイプフレームワーク
- django.contrib.sessions – セッションフレームワーク
- django.contrib.messages – メッセージフレームワーク
- django.contrib.staticfiles – 静的ファイルの管理フレームワーク
python manage.py migrate
migrateコマンドはINSTALLED_APPSの設定を参照するとともに、mysite/settings/pyファイルのデータベース設定に従って必要なすべてのデータベースのテーブルを作成する。 ※もしデフォルトのアプリケーション群の中に必要ではないアプリがある場合、migrateを実行する前にINSTALLED_APPSから適切な行をコメントアウトしたり削除したりしても良い。 - モデルの作成 モデルには自分が格納したいデータにとって必要不可欠なフィールドと、そのデータの挙動を収める。DjangoはDRY 則 に従っており、Djangoのモデルの目的は、ただ一つの場所でデータモデルを定義し、そこから自動的にデータを取り出すことにある。これはマイグレーションを含む。 これから開発するpollアプリでは、QuestionとChoiceの2つのモデルを作成する。pollにはquestionとpublication dateの情報がある。choiceには選択肢のテキストとvoteという2つのフィールドがある。各Choiceは1つのQuestionに関連付けられている。 Djangoではこうした概念をPythonクラスで表現できる。polls/models.pyファイルを以下のように編集する。
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
各モデルは一つのクラスで表現され、いずれもdjango.db.models.Model のサブクラス。 各フィールドは Field クラスのインスタンスとして表現されている。例えば、 CharField は文字のフィールドで、 DateTimeField は日時フィールド。こうしたクラスは、各フィールドにどのようなデータ型を記憶させるかをDjangoに教える。 Fieldインスタンスそれぞれの名前(例:question_text , pub_date)は、機械可読なフィールド名で、このフィールド名はPythonコードで使うとともに、データベースも列の名前として使うことになる。 Fieldの最初の位置引数には、オプションとして人間可読なフィールド名も指定できる。 (例:pub_date = models.DateTimeField(“date published“)) Fieldクラスの中には必須の引数を持つものもある。例えばCharFieldにはmax_lengthを設定する必要がある。 Fieldクラスはいくつかオプションの引数もとれる。例として今回の場合は、viteのdefault値を0に設定した。 また、ForeignKeyを使用してリレーションシップが定義されている。これはそれぞれのChoiceが一つのQuestionの関連付けられることをDjangoに伝える。 - モデルを有効にする pollsアプリケーションをインストールしたことをプロジェクトに教える必要がある。 アプリケーションをプロジェクトに含めるには、構成クラスへの参照をINSTALLED_APPS設定に追加する必要がある。PollsConfigクラスには、polls/apps.pyにあるので、ドットつなぎのパスはpolls.apps.PollsConfigとなる。mysite/settings.pyを編集し、INSTALLED_APPS設定にドットつなぎのパスを追加する。
INSTALLED_APPS = [ "polls.apps.PollsConfig", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ]
これでDjangoはpollsアプリケーションが含まれていることを認識できる。 次のコマンドを実行する。python manage.py makemigrations polls
すると次のようなものが表示される:Migrations for 'polls': polls/migrations/0001_initial.py - Create model Question - Create model Choice
makemigrationsコマンド:モデルに加えられた変更を検出し、それを新しいマイグレーションファイルとして生成する。- Djanogoが過去のマイグレーションファイルから前回のモデルの状態を再構築。
- 現在のmodels.pyの内容と、上記で再構築した前回のモデルの状態を比較。
- 変更点が検出されるとその変更を適用するためのマイグレーションコードが新しいマイグレーションファイルに生成される。
python manage.py sqlmigrate polls 0001
すると次のような結果が表示される。BEGIN; -- -- Create model Question -- CREATETABLE "polls_question" ( "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "question_text" varchar(200)NOT NULL, "pub_date"timestamp with time zone NOT NULL); -- -- Create model Choice -- CREATETABLE "polls_choice" ( "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "choice_text" varchar(200)NOT NULL, "votes" integer NOT NULL, "question_id" bigint NOT NULL ); ALTERTABLE "polls_choice" ADDCONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id" FOREIGNKEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED; CREATEINDEX "polls_choice_question_id_c5b4b260"ON "polls_choice" ("question_id"); COMMIT;
※正確な出力は、使用しているデータベースによって異なる。上記の例は、PostgreSQLの場合に生成されるもの。 ※テーブル名はアプリケーションの名前 (polls) とモデルの小文字表記 の question と choice を組み合わせて自動的に生成される。 ※主キー (primary key, ID) は自動的に追加される。 ※便宜上、 Django は外部キーのフィールド名に “_id” ****を追加する。 ※通常FOREIGN KEY制約は、データがデータベースに挿入されるとすぐにチェックされる。しかし場合によってはこのチェックをすぐに行わず後で行いたい場合がある。DEFERRABLEはそのようなケースに使用される。具体的には、DEFERRABLEを使用すると制約チェックをトランザクションの終了時まで遅延することができる。トランザクションとは、1つ以上のデータベース操作をひとまとめにしたものを指す。 ※splmigrateコマンドは実際にはデータベースにマイグレーションを実行しない。ただ、Djangoが必要としているSQLが何であるかをスクリーンに表示するだけ。これはDjangoが何をしようとしているかを確認したり、データベース管理者に変更のためのSQLスクリプトを要求されているときに役立つ。 migrateを再度実行し、モデルのテーブルをデータベースに作成する。python manage.py migrate # 以下出力結果 Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Rendering model states... DONE Applying polls.0001_initial... OK
migrateコマンドはすべての適用されていないマイグレーションを捕捉してデータベースに対してそれを実行する。重要なのは、モデルに対して行った変更はデータベースのスキーマに同期するということ。 モデルの変更を実施するための3ステップガイド- モデルを変更する(models.pyの中の)
- 変更のためのマイグレーションを作成するために
python manage.py makemigrations
を実行する - データベースにこれらの変更を適用するために
python manage.py migrate
を実行する
マイグレーションの作成と適用のコマンドが分割されている理由は、マイグレーションをバージョン管理システムにコミットし、アプリと共に配布するため。これによって、開発が容易になるだけでなく他の開発者や本番環境にとって使いやすいものになる。
- APIで遊んでみる API:ソフトウェア同士をつなぐ。基本的なプロセスは「リクエスト(要求)」と「レスポンス(応答)」で構成される。例えば、Web APIの場合はログイン情報、クレジットカードの決済情報など個人情報にかかわる認証処理、画像人s機システム、データ分析AIといった高度で複雑な情報処理がAPIを通してカード会社やITプラットフォームにアクセスすることで可能になる。 Python対話シェルを起動して、Djangoが提供するAPIで遊んでみる。以下のコマンドを実行する。
python manage.py shell
シェルに入ったらデータベース API の世界を探検してみる!*# Import the model classes we just wrote.* >>> from polls.models import Choice, Question *# No questions are in the system yet. >>>* Question.objects.all() <QuerySet []> # Create a new Question. # デフォルトの設定ファイルではタイムゾーンのサポートが有効になっている。 # そのため、Djangoはpub_dateに対してtzinfo(タイムゾーン情報)を持つdatetimeを期待している。 # datetime.datetime.now()の代わりにtimezone.now()を使用すると、適切に行われる。 >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # Now it has an ID. >>> q.id 1 # 1が表示されない場合は、q.save() を実行してから再度 q.id してみる。 # Pythonの属性を通じてモデルのフィールド値にアクセスする。 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2023, 8, 20, 13, 14, 40, 759205, tzinfo=datetime.timezone.utc) # 2023年8月20日13時14分40.759205秒 # 属性の値を変更して、その後save()を呼び出すことで値を変更する。 >>> q.question_text = "What's up?" >>> q.save() # objects.all()はデータベース内のすべての質問を表示する。 >>> Question.objects.all() <QuerySet [<Question: Question object (1)>]>
Question: Question object (1)は、このオブジェクトの表現としてまったく役に立たない。polls/models.pyファイル内にあるQuestionモデルを編集してこれを修正する。str()メソッドをQuestionとChoiceの両方に追加する。import datetime from django.db import models from django.utils import timezone class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") def __str__(self): return self.question_text def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text
import datetimeとfrom django.utils import timezoneを追加した。これは、Pythonの標準モジュールdatetimeとDjangoのタイムゾーン関連ユーティリティのdjango.utils.timezoneを参照するため。 変更を保存して、もう一度python manage.py shellを実行して新しいPython対話シェルを始める。 ※対話シェルを終了する場合は、exit()を実行する。>>> from polls.models import Choice, Question # Make sure our __str__() addition worked. >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # Djangoはキーワード引数によって駆動される豊富なデータベース検索APIを提供している。 >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> >>> Question.objects.filter(question_text__startswith="What") <QuerySet [<Question: What's up?>]> # Get the question that was published this year. >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # Request an ID that doesn't exist, this will raise an exception. >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # 主キーによる検索は最も一般的なケースであるため、Djangoは主キーの正確な検索のためのショートカットを提供している。 # 以下は Question.objects.get(id=1) と同じ。 >>> Question.objects.get(pk=1) <Question: What's up?> # Djangoのモデル内で定義されたカスタムメソッドwas_published_recently()が正しく動作するかを確認する。 >>> q = Question.objects.get(pk=1) # データベースからQuestionモデルの主キー(pk)が1のオブジェクトを取得し、それを変数qに格納する。 >>> q.was_published_recently() # 先ほど取得したQuestionオブジェクトqのwas_published_recently()メソッドを呼び出す。このメソッドは、質問が最近(このコンテキストでは恐らく1日以内)に公開されたかどうかを確認している。 True # Give the Question a couple of Choices. # create呼び出しは新しいChoiceオブジェクトを構築し、INSERT文を実行し、選択肢を利用可能な選択肢のセットに追加し、新しいChoiceオブジェクトを返す。 # DjangoはForeignKey関係の"other side"を保持するためのセットを作成する。 >>> q = Question.objects.get(pk=1) # Display any choices from the related object set -- none so far. >>> q.choice_set.all() <QuerySet []> # Create three choices. >>> q.choice_set.create(choice_text="Not much", votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text="The sky", votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text="Just hacking again", votes=0) # Choice objects have API access to their related Question objects. >>> c.question <Question: What's up?> # And vice versa: Question objects get access to Choice objects. >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 # The API automatically follows relationships as far as you need. # Use double underscores to separate relationships. # This works as many levels deep as you want; there's no limit. # Find all Choices for any question whose pub_date is in this year # (reusing the 'current_year' variable we created above). >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> # Let's delete one of the choices. Use delete() for that. >>> c = q.choice_set.filter(choice_text__startswith="Just hacking") >>> c.delete() (1, {'polls.Choice': 1})
- Django Admin
- 管理ユーザーを作成する 以下のコマンドを実行する。
python manage.py createsuperuser
好きなユーザー名を入力する。Username: admin
emailアドレスを入力する。Email address: admin@example.com
パスワードを2回入力する。Password: ********** Password (again): ********* Superuser created successfully.
- 開発サーバーの起動 Django adminサイトはデフォルトで有効化される。開発サーバーを起動してみる。 もしサーバーが起動していなかったら下記のコマンドを実行して起動する。
python manage.py runserver
次にブラウザを起動してhttp://127.0.0.1:8000/admin/ にアクセスする。ユーザーネームとパスワードを入力してログインする。 - pollアプリをadmin上で編集できるようにする pollsアプリはadminのインデックスページを見ても表示されていない。 Questionオブジェクトがadmin インタフェースを持つということを、adminに伝える必要がある。polls/admin.pyを開いて下のように編集する。
from django.contrib import admin from.models import Question admin.site.register(Question)
これで管理画面にpollsアプリが表示される。 - adminの機能を探究してみる “Questions” をクリックすると、questions のための “change list” ページが表示される。このページにはデータベース中のすべての question が表示され、その中のひとつを選んで変更することができる。
What’s up?を編集する。
「今日」や「現在」ショートカットをクリックして、「Date published」を変更してみる。変更したら、「保存して編集を続ける」を押す。次に、右上にある「履歴 (History)」をクリックしてみると、ユーザが管理サイト上でオブジェクトに対して行った変更履歴の全てを、変更時刻と変更を行ったユーザ名付きでリストにしたページが表示される:
- 管理ユーザーを作成する 以下のコマンドを実行する。
コメント