Vagrant – Django – サーバーエラー

概要

以下のような流れでエラー発生・解決した記録。データベース接続なしでとにかくDjangoのサーバー起動とアクセスを確認。

  • Vagrant上のCentOS7にPython3やvertualenvをインストール
  • 仮想環境上でDjango開発用サーバーを起動
  • Exception発生
  • ひとまずデータベースを切ってサーバー起動
  • ブラウザーから接続できず
  • Vagrantfileを編集してIPアドレス変更
  • DisallowedHostで接続できず
  • settings.pyのALLOWED_HOSTを編集
  • 無事接続

データベースを繋がずにとにかくDjangoの応答までを確認する場合、Vagrant環境では以下のように設定。

  • Vagrantfileでポートマッピングを設定
  • VMでifconfig→eth0のIPアドレスを確認
  • settings.pyのALLOWED_HOSTSにブラウザーでアクセスしたいホスト名、IPアドレスを追加
  • 開発用Webサーバーをeth0のIPアドレスとマップしたポートで起動
  • ALLOWED_HOSTSで許可したホスト/IPアドレスとマップしたポートでアクセス

サーバー起動時エラー発生

Vagrant/CentOS7環境上にPython3virtualenvをインストールして、仮想環境を作成。その環境下でDjangoをインストール

とりあえずテスト用のプロジェクトを作成。

プロジェクトで開発用Webサーバーを起動しようとしたところ、エラー発生。SQLiteのバージョンが低いと言っているらしい。

SQLiteを確認したところ、3.7と必要なバージョンより確かに低い。

データベース無効化で再挑戦

そもそもデータベースにはSQLiteではなくMySQLを使っている。またとりあえずの動作確認なので、データベースを設定せずにサーバーを起動してみようと考える。

Laravelでいえば.envファイルにあたる設定ファイルは、Djangoではsettings.pyらしい。その場所を探してみるとプロジェクトディレクトリーと同名のサブディレクトリーにあった。

settings.pyの中のデータベース関係を探して、そこをすべてコメントアウトしてみる。

再度サーバーを起動すると、エラー無く実行された。

ブラウザーで接続不可

サーバーは起動されたが、ブラウザーでhttp://127.0.0.1:8000とするとアクセスできない。

Vagrantのプライベートネットワークに沿った設定とサーバー起動が必要なことを忘れていた。

プライベートネットワーク設定

Vagrantfileの内容を確認・編集

vagrant reloadで再起動し、vagrant sshでCentOS7に入り、ポート設定を見る。

ゲスト側のVMからは10.0.20.15でサーバーを立ち上げ、ホスト側では127.0.0.1で接続する。ポートフォワーディングはホスト・ゲストとも8000

開発用Webサーバーを10.0.20.15:8000で起動。

Djangoで許可されない

ブラウザーでhttp://127.0.0.1:8000http://localhost:8000とするが接続できない。ただしDjangoによって許可されていないらしく、ALLOWED_HOSTSを加えるよう示されている。

ALLOW_HOSTSを編集して接続

settings.pyALLOWED_HOSTSを編集。

ブラウザーからhttp://127.0.0.1:8000あるいはhttp://localhost:8000でアクセスでき、以下のページが表示された。

 

 

 

Linux – bash – シェルプロンプトの変更

プロンプトの書式の確認

Vagrant上のCentOS7でシェルはbash。プロンプトは以下のようになっている。

この書式はPS1変数で設定されている。

それぞれの意味は以下の通り。

  • \u:ユーザー名
  • \h:ホスト名
  • \W:カレントディレクトリー

プロンプトの変更例

$#だけにする

プロンプトの後にスペースを入れておく。

ユーザー名と時刻にする

一部に色を付ける

以下はユーザー名と時刻を表示して、ユーザー名を青にしている(ターミナルではvagrantが青で表示される)。

常時設定する

~/.bashrcexport文を加える。

プロンプト書式に使える記号

以下の記号が使える。

  • \d:日付
  • \h:ホスト名の最初のブロック
  • \H:ドメイン名まで含めたホスト名
  • \s:シェルの名前
  • \t:時間~24時間制
  • \T:時間~12時間制
  • \@:時間~AM/PM形式
  • \u:ユーザー名
  • \w:カレントディレクトリー
  • \W:カレントディレクトリーのベース名
  • \!:コマンドの履歴番号
  • \$:一般ユーザーは$、rootなら#
  • \\:バックスラッシュ

色の設定

色設定はエスケープシーケンスで行う。

  • 設定開始:[ESC]+'['+[色コード]+'m'
  • 設定解除:[ESC]+'['+'39'+'m'

色コードには前景色と背景色がある。

  • 前景色:30~37、90~97
    • 90~97は明るめの色
  • 背景色:40~47
    • 100~107は明るめの色
  • 拡張指定:38
    • 256カラーレベル:38;5;n、48 ;5;n
    • RGB指定:38;2;r;g;b、48;2;r;g;b
  • 色設定解除:39
前景色 背景色
Black 30 40
Red 31 41
Green 32 42
Yellow 33 43
Blue 34 44
Magenta 35 45
Cyan 36 46
White 37 47

 

PHP – 多次元連想配列のソート

概要

キー列が同じ連想配列を要素とする配列の要素を特定のキーに従ってソートするには、以下のようにarray_column()array_multisort()を使う。

問題

以下のような配列があり、この配列の要素をnameageなどで並べ替える方法。

array_multisort()を使う

array_multisort()は第1引数指定した1次元配列の順番に従って、第3引数の配列の要素をソートする。第2引数にはソートの昇順/降順を指定する。

たとえば以下の例では、2つの要素を持つ連想配列の配列を並べ替えている。

この例の流れは以下のとおり。

  • 第1引数の配列[3, 2, 1]の各要素が$aの各要素に対応している
  • 第2引数で昇順(SORT_ASC)を指定
  • 第3引数の配列がこれに従って並べ替えられ、内容が変更される

なお第1引数と第3引数の次数が等しくない場合はWarningとなり、第3引数の配列は変更されずに処理が進む。

また、第1引数の配列の要素が文字列の場合は辞書順で並べ替えられる。

array_column()

array_multisort()の第1引数に第3引数のあるキー列を選べば、そのキー列に従って並べ替えられる。

問題の配列の'age'のキー列は以下のとおり。

手順

このキー列の値の昇順で元の配列を並べ替えるには、第1引数に上のarray_culumn()を適用すると、'age'の昇順で配列が並べ替えられる。

降順に並べ替えたいときはSORT_DESCを指定する。

 

HTML – input – file

基本形

ファイルアップロードは、FORM要素とINPUT要素のそれぞれで属性の設定が必要。

FORM要素は以下のように記述する。

  • methodPOST
  • enctype属性に"multipart/form-data"を設定する

INPUT要素は以下のように記述する。

  • type属性を"file"に設定する
  • methodPOST
  • name属性を設定する
    • この属性値で言語やフレームワークによってファイルを扱う
    • 以下の例ではname属性値を"file"としている

この場合の動作は以下のとおり。

  • "ファイルを選択"ボタンを押すとダイアログがファイル選択ダイアログが表示される
  • 一度に1つのファイルを選択できる
  • ファイルを選択して"開く"ボタンを押すとファイルがアップロードされる

複数ファイルの選択

基本形ではダイアログで1つのファイルしか選択できない。複数ファイルを選択・アップロードできるようにするには、INPUT要素を以下のように記述する。FORM要素に変更はない。

  • type属性を"file"に設定する
  • methodPOST
  • name属性の値の末尾に[]を付けて配列にする
    • 以下の例ではname属性値を"files[]"としている
  • multiple属性を追加

これによってファイル選択ダイアログで複数のファイルが選択・送信できるようになる。

 

Laravel – ルーティングが効かない

現象

routes/web.phpにルーティングを追加したが、そのルーティングが機能せずブラウザの表示が真っ白になってしまった。

原因

以下のような順番でルーティングを書いたため。

理由

URLに.../products/select_typeが与えられると、まずリソースルーティングのところで.../products/{id}として解釈されてしまい、以降のルーティングに到達しないため。

解決

ルーティングの順番を再考する。基本的に個別ルーティングはリソースルーティングより前に置く。

 

 

PHP – new staticとnew self

概要

staticselfnewと組み合わせることでクラス内でインスタンスを生成する際に使えるが、以下のような違いがある。

  • new staticはそれが実行されるときのクラスのインスタンスを生成する
  • new selfはそれが定義されたときのクラスのインスタンを生成する

参照:static::とself::

準備

以下のようにParentClassとそれを継承したChildClassを準備する。

  • いずれも同じ名前のインスタンスメソッドmethod()を持つ
  • それぞれのクラスはスタティックメソッドparent_method()child_method()を持つ
  • 何れの内容も同じで、以下を実行する
    • (new static)->static_method()
    • (new self)->static_method()

new staticnew selfはクラスのインスタンスを生成する。この例では()->で生成されたインスタンスから直接メソッドを呼び出しているが、$instance = new staticのように一旦変数に参照させて$instance->method()としてもよい。

親クラスのメソッドで生成する場合

親クラスのメソッド内でstaticselfで生成されるインスタンスは親クラスのインスタンス。

子クラスのメソッドで生成する場合

子クラスのメソッド内でstaticselfで生成されるインスタンスは子クラスのインスタンス。

継承元の親クラスのメソッドで生成する場合

子クラスから継承元の親クラスのメソッドを呼んで、その中でstaticselfで生成されるインスタンスは以下の通り。

これらは以下のような動作による。

子クラスから呼ばれた親クラスのスタティックメソッドで以下が実行される。

new staticで生成されるのは、この文が実行されるときにスタティックメソッドを呼び出したChildClassなので、ChildClassのインスタンスが生成されて、そのmethod()が呼ばれる。

new selfで生成されるのは、この文が定義されたParentClassなので、ParentClassのインスタンスが生成されて、そのmethod()が呼ばれる。

 

Laravel – RegisterController

概要

RegisterControllerは、Laravelのプロジェクト開始時にapp/Http/Controllers/Authディレクトリー下に生成される。

ルーティングファイルにAuth:routes()を記述することで、このコントローラーの各アクションは以下のようにURLと結び付けられる。

method URL action name
GET /register showRegistrationForm register
POST /register register

ソースコード

内容

コントローラーの機能

ソースコード冒頭のコメントの要旨は以下の通り。

  • このコントローラーは、新規ユーザーの登録、入力バリデーション、データ作成とともに行う
  • デフォルトではこの機能をIlluminate\Foundation\Auth\RegistersUsersトレイトで実装し、追加のコードなしで上記の機能を提供

RegistersUsersトレイトの導入

登録後の遷移先

登録後の遷移先を設定している。

コントローラーの生成

コントローラー生成時のコンストラクター。

入力バリデーション

メールアドレスをユーザーIDとして使うため、入力バリデーションでemailuniqueの制約をかけている。

Userモデルインスタンスの作成

ユーザー登録時、パスワードにHash()メソッドを適用して暗号化している。

 

Laravel – $this->guard()

 

ログイン認証の例

たとえばログイン認証のLoginControllerに使われるIllumination\Foundation\Auth\AuthenticatesUsersトレイトlogin()メソッドは次のような内容。

この中で呼ばれているログイン認証処理のattemptLogin()メソッドは、同じトレイトで定義されている。

ここでguard()というメソッドが出てくる。これは同じAuthenticatesUsersトレイトの最後の方で以下のように定義されているが、そもそもどういうものかよくわからなかったので掘り下げてみたら、どんどん深みにはまってしまった。

guard()が見当たらない

AuthenticatesUsersトレイトでAuth::guard()が呼ばれているので、まずAuthクラスについて調べてみる。

AuthクラスはAuthenticatesUsersトレイトの最初で以下のようにuse定義されている。

そこでIlluminate\Foundation\Authクラスを見てみると、コメントにstaticメソッドらしきものが書かれているが、クラス定義の本体にはguard()メソッドが見当たらない。

Authクラスの継承元のIlluminate\Support\Facades\Facadeクラスも見てみたが、useしているトレイトも含めてguard()メソッドは定義されていない。

マジックメソッド__callStatic()を使っている

__callStatic()の内容

いくつかのサイトを参考にしたところ、__callStatic()というマジックメソッドを使っていることがわかってきた。このマジックメソッドは、クラスに定義されていないstaticメソッドが呼ばれた際に実行される。

Facadeクラスを見てみると、以下のように__callStatic()が定義されていた。

最初にあったAuth::guard()スタティックメソッドを呼ぶと、このマジックメソッドが呼ばれ、引数の$method'guard'$argsは空の配列となる。

以下で、この__callStatic()の内容を読んでいく。

getFacadeRoot()

メソッドの最初でgetFacadeRoot()メソッドが呼ばれている。最終的に$instanceにはAuthManagerのインスタンスが返されるが、その道筋を追ってみた。最後の部分だけがまだ理解できておらず、参考サイトの受け売りだが。

このメソッドは同じFacadeクラスで定義されていて、getFacadeAccessor()を引数に与えたresolveFacadeInstance()の戻り値を返している。

getFacadeAccessor()

ここでstaticメソッドgetFacaceAccessor()は、Facadeクラスとこれを継承したAuthクラスの両方で定義されている。

FacadeクラスでのgetFacadeAccessor()の定義は、単に「メソッドが定義されていない」という例外を返す。

一方、Facadeを継承したAuthクラスで定義されたgetFacadeAccessor()は文字列'auth'を返す。

getFacadeRoot()Facadeを継承したAuthクラスから呼ばれると、AuthクラスのstaticメソッドがFacadeクラスのstaticメソッドをオーバーライドして実行される。

この結果、resolveFacadeInstance()の引数には文字列'auth'が与えられる。

resolveFacadeInstance()

引数に'auth'を与えられたresolveFacadeInstance('auth')の挙動を見てみる。

流れは以下の通り。

  • 引数$nameの内容は文字列なので、1つ目のifブロックはスルー。
  • static::$resolvedInstanceFacadeで定義されていて、$nameに対応したインスタンスがあればそのインスタンスが得られる。
  • もしインスタンスが登録されていなければ、最後のifブロックでstatic::$app['auth']の内容が登録されて、そのインスタンスが返される。

この$app['auth']がわからなかった。こちらのサイトの内容をひとまず丸呑みする。

  • static::$appはヘルパーメソッドapp()と同じ動作をする
    • static::$app[‘auth]app()[‘auth’]と同じ意味
    • これはサービスコンテナからauthサービスのインスタンスが起動させる下記のコードと同じことを表す
  • app()->make(‘auth’)で作成されるサービスはIlluminate\Auth\AuthServiceProviderregisterメソッドによって登録されており、Immulminate\Auth\AuthMangaerがインスタンス化されている

__callStatic()の戻り値

$instanceが適正にセットされれば、_callStatic()は以下のように戻り値を返す。

__callStatic()が呼ばれたときの$methodguard()、引数は空だったので、戻り値のメソッドはAuthManager::guard()となる。

guard()はSessionGuard()になる

AuthManager::guard()

結局、attemptLogin()->guard()AuthManager::guard()となり、その内容は以下の通り。

引数なしで呼ばれるので、$nameAuthManager::getDefaultDriver()となる。その内容は以下の通り。

このメソッドは、configディレクトリーにあるauth.phpのdefaultsのキー'guard'に対する値を返す。デフォルトでその内容は'web'となっている。

そして、最後のreturn文にある$this->guards[$name]$this->guards['web']となるが、これが存在しない場合は??演算子の右側が評価されて、AuthManager::resolve('web')が内容としてセットされる。

AuthManager::resolve()

AuthManager::resolve()の内容は以下の通り。

最初の実行文で呼ばれるgetConfig('web')により、config/auth.phpguardsセクションの'web'キーに対応する値が$configとしてセットされる。

auth.phpの該当する部分は以下の通り。

そして、$driverMethodに文字列連結で生成された文字列'createSessionDriver'が格納される。

createSessionDriver()メソッドはAuthManagerに存在するので、ifブロック内の$this->createSessionDriver($name, $config)が実行される。

createSessionDriver()を見ると、$guardとして\Illuminate\Auth\SessionGuardクラスのインスタンスが格納される。

SessionGuardクラスはattempt()メソッドを持つので、最初のattemptLogin()メソッドは以下を実行していることになる。

参考サイト

 

Laravel – REST~リソースベース

概要

RESTはREpresentational State Transferの略。ここでは以下のような感じで理解しておく。

  • リソースを一意に特定できるURL
  • GET、POSTなどのメソッド
  • ステートレスで完結

Laravelでは、リソースベースでルーティングを記述し、リソースコントローラーを生成することで、RESTに沿ったコードが書ける。

以降、Itemというモデルを仮定する。Itemモデルのインスタンスをitem、その集合体をitemsとする。

リソースを一意に特定するURL

サーバーにモデルの集合体が保存されていて、これら全体あるいはその中の特定のデータや、データを表示するビューなどのリソースを指定するURLの書き方。

/items
itemのインスタンスの集合体あるいはこれらを表示するビューなどのリソース。
/items/create
1つのitemを作成するための、入力フォームを含むビューなどのリソース。
/items/{id}
itemsのうちidで特定される1つのitemやこれを表示するビューなどのリソース。POST、PATCH、DELETEといったメソッドによってitemに対する操作を指定する。
/items/{id}/edit
idで特定されるitemの内容を編集するフォームを含むビューなどのリソース。

メソッド

GET
URLで指定したビューのHTMLなどのリソースをサーバーに要求する。
POST
URLで指定したリソースの内容をサーバーに送信する。
PATCH
URLで指定したリソースの内容でサーバー上のリソースの内容を変更するよう要求する。
DELETE
URLで指定したリソースをサーバー上から削除するよう要求する。
PUT
指定した内容でサーバー上のリソースを置き換える。LaravelのRESTfuLの枠組みでは使われない。

RESTfuLなルーティング

メソッドとURLを組み合わせることで、サーバー上のリソースに対する操作が確定する。LaravelでのRESTfuLな操作は以下の7つで、それぞれに応じたコントローラーのアクションにルーティングするのが標準。

リソースベース

リソースベースの考え方

RESTfuLなルーティングと、これに対応したアクションを自動的に生成する方法。

  • リソースルーティングの書き方によって、7つのRESTfuLなルーティングを1行で記述
  • artisanのコントローラー作成コマンドの--resourceオプションで、RESTfuLなルーティングに対応した7つのアクションを持つコントローラーを生成

リソースルーティング・名前付きルート

ルーティング定義に以下の1行を書くだけで、RESTfuLな7つのルーティングを書いたのと同義になる。

routes/web.php

このとき各ルートに名前も付けられる

一覧で確認。

リソースの限定

以下のように書くことで、特定のリソースのみを選んだり除外したりできる。

リソースコントローラー

以下のartisanコマンドで、RESTfuLなルーティングに対応したメソッドを持つコントローラーが生成される。

php artisan make:controller ItemController --resource

生成されるコントローラーのアクションは以下のとおり。

  • index()
  • create()
  • store(Request $request)
  • show($id)
  • edit($id)
  • update(Request $request, $id)
  • destroy($id)

app/Http/Controllers/ItemController

 

Laravel – 例題 – Rock-Paper-Scissors

概要

LaravelのToy problemとして、簡単なじゃんけんのアプリケーションを作る。

  • 初期画面にRock/Paper/Scissorsの3つのラジオボタンと”Match!”ボタンを表示
  • ユーザーがラジオボタンのいずれかを選択して”Match!”ボタンを押すと、コンピューター側の手をランダムに発生させて勝敗比較
  • ユーザー/コンピューターの名前と手、結果を表示に加えて再表示

ユーザー、コンピューターのオブジェクトを生成するPlayerクラスを作成する。

また、手の値のバリデーションは行っていない。

Playerクラス

ユーザー/コンピューターに関わらず、プレーヤーの名前と現在の手(Rock/Paper/Scissors)を保持し、勝ち負け・あいこの判定機能を備えるクラス。

クラスファイルはpublic下のサブディレクトリ―に置く

  • クラスファイルの配置はpublic/rock_paper_scissors/Player.php
  • コントローラーなどからアクセスする場合、public_path()を使う

クラスの仕様は以下の通り。

  • 名前空間を含むフルパスのクラス名はrock_paper_scissors\Player
  • このクラスからユーザーとコンピューターそれぞれのインスタンスを生成
  • Playerのプロパティーは名前($name)とじゃんけんの手($hand_number)
  • $hand_numberは0: rock、1: paper、2: scissorsに対応
  • $hand_numberは外部からpublicメソッドで設定
  • $hand_numberに対応した手の名称をクラス定数の配列として準備し、現在の手の名前を返すメソッドを準備
  • 自身が他のPlayerに対して勝っているか(wins)、あいこか(draws)を判定するメソッドをそれぞれ準備

ルーティング

ルーティングはGETメソッドとPOSTメソッドに対応して2つ。

コントローラー

コントローラーのアクションは、GETに対する初期ページへの遷移と、POSTに対する処理・遷移の2つ。

ビュー

GETの場合はフォームだけ表示し、POSTでじゃんけんを実行したときには結果も表示する。

実行結果

初期表示

Rock-Paper-Scissors

  

 

結果表示

Rock-Paper-Scissors

  


User

  1. Name: USER
  2. Hand: Rock

Computer

  1. Name: COM
  2. Hand: Paper

Result

Computer won