続いては、いよいよアプリの作成です。ここからが自作部分ですね。
本稿で取り扱うアプリの構成は、その設計、もっと言えば設計の思想に大きく影響されてしまいます。そしてその思想は完全なる正解を得るのが難しいのでその部分についてはスルーしていただき、「あれ」をやりたいんだけど「こう」すれば良いのか、というその部分だけをくみ取っていただけるとよろしいかと思います。
さらに言えば、どこのベストプラクティスにも従っていないので、情報の活用には十分ご注意下さい。
STEP 1 アプリの作成
アプリケーション”app”の作成をします。appというのは私が名付けたアプリ名なのでご自身で何らか名付けて下さい。
python manage.py startapp app
今回このアプリ(app)の役割は、この中で何でもかんでもやってしまうという想定です。
個人的によくやるのは、1つ目のアプリとしてapiというアプリを作成します。モデルは基本的にこのアプリ内で構成し、あわせてREST APIもこのアプリで提供します。そしてフロントエンドをJavaScirptによってSPA(Single Page Application)として構成する場合などではこのアプリだけでDjangoのアプリは終わってしまうことも有ります。Web画面をDjango側で持つ場合には、これに加えてappアプリを追加で作成したりもします。
中には、データモデルだけのアプリを作る方もいらっしゃるかも知れません。はい、完全なる設計の思想の話になってしまいます。
なお、しばらくはこのall-in-oneなappで話が進みますが、永遠と開発の追体験を続けられないので、どこかのタイミングでapiもappもある状態に説明例が混ざることがあり得ますがご了承ください。
本線に戻りますと、appディレクトリが作成されたと思います。
(LearningDjango) MacBookPro:houjin $ tree . ├── app │ ├── init.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── init.py │ ├── models.py │ ├── tests.py │ └── views.py ├── houjin │ ├── init.py │ ├── pycache │ │ ├── init.cpython-38.pyc │ │ ├── settings.cpython-38.pyc │ │ ├── urls.cpython-38.pyc │ │ └── wsgi.cpython-38.pyc │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── log │ └── django.log ├── manage.py └── tmp 6 directories, 18 files (LearningDjango) MacBookPro:houjin $
appディレクトリの配下にこんなに自動生成してくれるんですが、実は1つ手作業があります。houji/settings.pyファイルにINSTALLED_APPSという設定があります。そこにappを追記します。もちろんappと名付けなかった人は、名付けた通りの名前にして下さい。(デフォルトの動きとして追記してくれて、必要ない場合だけ除去する方が幸せの量が不得手そうなんですが、何故なんですかね?)
【サマリー】新規Djangoアプリの作成
キーワード: 新規 Django アプリ 作成
python manage.py startapp app
※プロジェクト名/settings.pyのINSTALLED_APPSへの追加を忘れずに!
STEP2 モデルの作成
モデルの作成では、文字通りモデルの作成とそれをDBへ反映させるところまでを扱います。「モデルを作成する手順」という言葉には、なんでも良いから作ってみる=作成という行為に共通する手順を指す部分と、カラムの属性をこうしたい、2カラム組み合わせのインデックスを貼りたい、のようにとても具体的にDBでの構成とモデルでの記述の対応のように個別の仕様に関する部分の両方が含まれしまいますが、ここでは前者を扱い、後者のモデル自身の詳しい解説は記事を改めたいと思います。
解説: models.py
編集するのはmodels.pyファイルです。中身が事実上空っぽで「ここに君のモデルを作りたまえ」というコメントが見えているかと思います。ここにモデルを記載します。
モデルというのは、DBのテーブルに対応します。上記の例だとcompanyというテーブルに、corporate_numberとnameというカラムが作成され、それらはvarcharで構成されるようになります。
Companyなのにcorporateで裸でnameかよと、命名がイマイチなのは中途半端に開発中のアプリからコピペしてきたからで、突っかからずにご自身のつけた立派な命名で是非お願いします。
【サマリー】Djangoモデル作成
キーワード: Django モデル 作成 サンプル
from django.db import models # Create your models here. class Company(models.Model): """法人データ""" corporate_number = models.CharField('法人番号', max_length=16, db_index=True) name = models.CharField('法人名', max_length=512, blank=False, null=False)
さて作成したモデルを実際にDBに反映させてみます。
まずはmakemigrationsからです。
python manage.py makemigrations app
(LearningDjango) MacBookPro:houjin $ python manage.py makemigrations app [DEBUG] utils.py: 123: debug_sql (0.000) SELECT @@SQL_AUTO_IS_NULL; args=None [DEBUG] utils.py: 123: debug_sql (0.001) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None [DEBUG] utils.py: 123: debug_sql (0.001) SHOW FULL TABLES; args=None [DEBUG] utils.py: 123: debug_sql (0.001) SELECT `django_migrations`.`id`, `django_migrations`.`app`, `django_migrations`.`name`, `django_migrations`.`applied` FROM `django_migrations`; args=() Migrations for 'app': app/migrations/0001_initial.py - Create model Company (LearningDjango) MacBookPro:houjin $
これでDBへ反映するための準備が出来ました。具体的には0001_initial.pyが作成されました。この事実は覚えておいて損は無いと思います。「私、失敗しないので」とかなら別ですが。
つづいてmigrateです。
python manage.py migrate app
makemigrationsのところで書き漏らしましたが、末尾のappは指定しなくても動作します。appに関する操作だけをしたい場合に指定し、指定しなかった場合にはプロジェクト内全体についてその操作が行われます。必要に応じて使い分けをして下さい。
実行例の掲載は、長々とSQLのデバッグログが表示されるだけなので割愛します。
さて、実際にDBを見て見ますと、app_companyというテーブルが全部で3カラム構成されていると思います。
これを見ると分かるとおりidはauto_incrementなプライマリーキーなカラムを勝手に構成してくれるのでモデル作成時には不要です。むしろ自身で設計したカラムをプライマリーキーとして使いたい場合には別の手立てが必要ということですね。
【サマリー】Django モデルのDB反映
キーワード: Django モデル DB 反映 makemigrations migrate
python manage.py makemigrations app python manage.py migrate app
STEP 3 URLディスパッチャーの構成
ブラウザからのリクエストがDBへたどり着き、その後画面を返す流れを想像すると、真っ先に到達するのがhoujin/urls.pyです。(実際にはその前段に共通処理を差し込んだりも出来ますが、少し先の話にとっておいて下さい)
解説: houjin/urls.py
プロジェクトのurls.pyという言い方をここでします。というのはすでにスクリーンショット上に出ていますが、appのurls.pyもこの後作成するからです。
include()となっているとおり「app/宛てに飛んできたらapp/urls.pyよろしくー」っていうだけです。
解説: app/urls.py
さてappのurls.pyの方ですが、pathに指定するのはプロジェクトのurls.pyの続きです。なので実際には/app/index.htmlに対するアクセスという定義になっています。ここで2つ目の定義でviews.topとなっていることからも分かるとおり、このあとapp/views.pyにtopという名称の関数を作成し、そちらに処理させることになります。
解説: app/views.py
先ほど処理を任せる先としてtopという名称の関数を指ししてしましたが、その実装部分です。ここではなんとExceptionを発生させています。処理の核心部分でもあるDBまで到達する手順を示しても良いのですが、ここまでに間違いがあると意味がありませんし、意外と間違うところでもあるので、一旦ここで成功体験を得るのを目的にビューの中を簡単にしています(ま、成功してもエラーなんですけど)
【サマリー】 Django URLディスパッチャー
キーワード: Django urls.py URL
プロジェクト名/urls.py
from django.contrib import admin from django.urls import path from django.urls import include urlpatterns = [ path('admin/', admin.site.urls), path('app/', include('app.urls')), ]
アプリ名/urls.py(新規作成)
from django.urls import path from app import views app_name = 'app' urlpatterns = [ path('index.html', views.top, name='top_page'), ]
ここまで実装できたら開発用サーバーを起動して実際にアクセスして「自爆」するのを目撃します。なお、開発中runserverは起動しっぱなしで問題ありません。停止していた方は以下で起動して下さい。
python manage.py runserver
アクセス先は http://127.0.0.1:8000/app/index.htmlのはずです。
見事に「自爆」が目撃できました。自爆以外のエラーになった方は、途中何かが間違っています。もう一度精査して自爆できるまで頑張りましょう。
STEP 4 Viewの構成
前ステップではおふざけ半分にわざとエラーにしていましたが、app/views.pyを真面目な画面を返すように実装しましょう。
解説: views.py
2つのurls.py経てこの関数にリクエスト誘導されてきたものを、ここで処理を施し、messageの値がhelloというcontextを引数にindex.htmlに引き渡されています。これまた段階を踏んでということになりますが、本来であればここでモデルを使ってDBへアクセスするのが通常です。
このあとindex.htmlを作成する訳ですが、実際にはHTMLにpythonで扱っていた変数を埋め込むテンプレートと呼ばれるものを作成します。
STEP 5 テンプレートの作成
テンプレートはtemplatesディレクトリ内に配置するようになっていますので、この中にindex.htmlというファイルを作成します。
基本的にはHTMLとして記述して問題ありません。今回はmessage変数にhelloという文字列を格納しているので{{ message }}の部分がそれに差し替わるという動作をします。
実際に確認してみます。http://127.0.0.1:8000/app/index.html
やってみてあまりに素っ気なく、何のことだか分からないので少し後悔しました…
STEP 6 (少し脱線) 管理画面にCompanyモデルを
せっかくなので、前稿で触れていた自作アプリのモデルを管理アプリに登場させる方法をご紹介します。といっても、たいしたことは有りません。元々そのように作られているので、app/admin.pyファイルを以下のように編集するだけで登場します。
確認して見てます。 管理画面は http://127.0.0.1:8000/admin/ でしたね。
無事にCompanysが登場していますね。複数系が機械的にsを付けているのはご愛敬です。(だったらやんなきゃいいのに…)
せっかくなのでデータを追加して、そのあとViewを書き換えてそれを引き出してみたいと思います。
Company object(1)だなんて情けない名前になっていますが、これもモデルの定義(models.py)をきちんと記述すれば変えられます。ともかく今はレコードがきちんと作成できたことを確認して下さい。
STEP 7 Viewの再構成
Companyオブジェクトを取得するには以下のようにします。
companies = Company.objects.all()
ですが、この時点でSQLが発行されるわけではありません。もしこの時点でSQLが発行されるのだとすると、これだと全量取得になり日本全国の法人が登録されていると大変な話です。
ところが、この時点でSQLが発行されていないという事実はとても便利で、そのあとfilter()といった形で数珠つなぎで処理を記述していくことが出来ます。
companies = companies.filter(name='株式会社東西南北')
つまり、http://127.0.0.1:8000/app/company?name=xxx&city=yyyのようなリクエストを想像したときに、以下のようにnameがあればつなげて、cityがあればつなげて、無ければつなげないという処理を書くことが出来ます。
if 'name' in request.GET and request.GET['name'] != '': name = request.GET["name"] companies = companies.filter(name__icontains=name)
SQLが実行されるのは、おそらくですがlen(companies)の所かと思います。必要とされるギリギリまで評価(実行)されません(遅延評価)。
てさ、今回のデータの場合わざわざfilter()なんて使わずget()を使えば良かったのですが、上記を説明したいがためにあえて使ってみました。
その代償として、1つ以上ヒットして、その1つ目を使うという余計なコードが入っています。
実際にアクセスしてみます。http://127.0.0.1:8000/app/index.html
無事に取得できました。
ついでにですが、せっかくなのでcompaniesのまま渡してみたいと思います。
受け取るテンプレート側ですこしロジックが必要です。今回はテーブルにして行が増える構造にしてみます。
さて、実際に確認してみます。
ご自身で2つ目3つ目のレコードを追加して表示が増えることも確認してみて下さい。
【サマリー】 Django Viewとテンプレート
キーワード: Django views.py templates サンプル
アプリ名/views.py
from django.shortcuts import render from app.models import Company # Create your views here. def top(request): companies = Company.objects.all() companies = companies.filter(name='xxxx') context = { "companies": companies, } return render(request, 'index.html', context)
アプリ名/templates/index.html
<HTML> <BODY> {% if companies %} <table> <tr> <th>法人名</th> <th>法人番号</th> </tr> {% for company in companies %} <tr> <td>{{ company.name }}</td> <td>{{ company.corporate_number}}</td> </tr> {% endfor %} </table> {% endif %} </BODY> </HTML>
ここまででDjangoアプリとして一通り動くところまでたどり着きましたので「完全に理解した」というのは言い過ぎかも知れませんが「ちょっと理解した」(チョットじゃないです)気分は味わえましたでしょうか。
このあとはもうちょっと理解を深めるために、models.py、views.py、urls.pyをそれぞれ多少深めていきたいと思います。
続いてのコンテンツは、urls.pyを少し深めることになる静的ファイルを取り扱います。