もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

やる気がでないとき

こんにちは。

今日は別に技術的な話じゃないです。

やる気がでないって話。

夏バテなのか、それともプライベートが忙しくて疲れているのか。

とにかく仕事終わって、筋トレして、風呂入って、いつもならば勉強しよーとってなるんだけど、

「今日はやる気がでないなー」

となって、寝ちゃう。

今月はまさにそんな谷間の月。

やる気の出ない自分に焦燥感を覚えるけど、なかなか体は前を向かない。

ある人にこの気持を伝えたところ、

「そういうときもあるから、焦んなくていいよ」

っていう優しい言葉を頂いた。

すごく嬉しかった。

嬉しかった。。。けど、やはりこのままではダメだと思う。

僕はほんと馬鹿だから、そういった優しい言葉をもらうと、すぐ鵜呑みにする。

「あー今そういう時期だからしゃーねーなー」

ってなる。

これじゃあだめだ。

夏バテだなんだと前口上を述べたけど、そんなものは関係なくて結果だけを見ないといけない。

今年の最初に比べて完全にモチベーション下がっていて、確実に勉強時間も減少してる。

そしてそれをただ、疲れや暑さのせいにしてるだけ。





というわけで、また今日から勉強頑張ります!

暑いならば裸でクーラーガンガンかけてやりゃいいんだし、疲れてても5分でも10分でもやらねば。

続けることが大事。すっかり忘れてた。

今年の1月の気分で頑張るぞ!!



ちなみに同じこと(やる気が出ないこと)をさっき奥さんに言ったら、

「焦ったほうがいんじゃない?」

という厳しい言葉を頂いたw

なんというか心の中で思ってたことをぐさっと、曇りなき眼で言われたので、結婚してよかったと思った。

cakephp3のpaginateでsubqueryを使う

こんにちは。

簡単なのですが、忘れないようにメモ。

CREATE TABLE `tag_masters` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL COMMENT 'タグ名',
  `created` timestamp NOT NULL COMMENT '登録日',
  `modified` timestamp NOT NULL COMMENT '更新日', 
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tag_relations` (
  `tag_id` int(11) unsigned NOT NULL COMMENT 'タグID',
  `item_id` int(11) unsigned NOT NULL COMMENT 'アイテムID',
  `created` timestamp NOT NULL  COMMENT '登録日',
  PRIMARY KEY (`tag_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

こんな感じのタグテーブルがあったとして、cakephpのpaginateでデータを取ってきたい時に、各タグの紐づけ数を一緒に取るときはこんな感じになるようです。

<?php
$this->paginate = [
        'fields' => [
            'TagMaster.id',
            'TagMaster.name',
            'TagMaster.created',
            'TagMaster.modified',
            'TagMaster__count' => "(SELECT count(l.tag_id) FROM tag_relations AS l WHERE TagMaster.id = l.tag_id)"
        ]
];

cakephpがDBにSQLを発行する際、各カラムをEntity名__カラム名で別名を貼って発行するので、その命名に沿って指定すれば取れるってことでした。

良かった。解決。

Firebase試してみる(Cloud Storage編)

はじめに

こんばんは。

今週は子供が体調悪くなったりとか、奥さんが体調悪くなったりとか、自分が体調悪くなったりとかで、家族揃ってダウンした週でした。

なので、ブログもぎりぎりになりました。

で、今回は前回前々回の続きで、FirebaseのCloud Storageを試してみようと思います。

ちなみにCloudStorageへアップロードするやり方は以下の本には書いてないので、公式を見ながらやってみようと思います。

WEB+DB PRESS Vol.105

WEB+DB PRESS Vol.105

  • 作者: 小笠原みつき,西村公宏,柳佳音,志甫侑紀,池田友洋,木村涼平,?橋優介,大塚雅和,飯塚直,吉川竜太,末永恭正,久保田祐史,浜田真成,穴井宏幸,大島一将,桑原仁雄,牧大輔,池田拓司,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/06/23
  • メディア: 単行本
  • この商品を含むブログを見る

やってみた

Storageルールの変更

Storageはデフォルトではセキュリティルールが適用されているらしいです。

デフォルトではユーザー認証が必要になるみたいです。

とりあえず、今回は上げるところだけをやってみたいので、一旦全公開にするためルールを変更しました。

f:id:kojirooooocks:20180714012614p:plain

アプリUI変更

前回までのアプリで作っていた、撮影した写真のプレビュー画面に一つボタンを追加します。

f:id:kojirooooocks:20180714012625p:plain

このボタンが押されたら、Cloud Storageにアップロードしようと思います。

Pod Install

次に、FirebaseのツールをCocoaPods経由でインストールします。

以下を追加して pod installします。

platform :ios, '11.4'
target 'test01' do
  use_frameworks!

  # Pods for test01
  pod 'Firebase/Core'
  pod 'Firebase/Auth'
  pod 'Firebase/Storage' ←これついか
end

コード修正

次は実際のswiftファイルの修正です。

先程追加したボタンに対応する IBActionを作ります。

    @IBAction func didTouchUploadButton(_ sender: Any) {
        guard let imageData = UIImageJPEGRepresentation(imageView.image!, 1.0) else {
            return
        }

        // アップロード
        let storageRef = storage.reference(forURL: "URL")
        let imageRef = storageRef.child("image_01.jpg")
        imageRef.putData(imageData, metadata: nil)
    }

実行テスト

起動して写真を取り、実際に+ボタンを押すと、特になにもUIの変化はありません(作ってないので・・・)

f:id:kojirooooocks:20180714012715p:plain

f:id:kojirooooocks:20180714012731p:plain

なにやらあがってる!!

ちょっとダウンロードしてみます。

f:id:kojirooooocks:20180714012802p:plain

f:id:kojirooooocks:20180714012818j:plain

先程アップロードしたやつでした!

終わりに

実際に上げるまではすごく簡単でしたが、認証周りのときもそうですが、これもエラー処理やアップロード成功後の通知など、実際にはもっと作り込む必要があります。

そのへんが一番たいへんそう。。。

とりあえずできました!

あーアプリ楽しいなぁ。

Firebase試してみる(Authentication編)

はじめに

こんばんは。

風呂入って、子供寝かして、ゆっくりしてたら日をまたいでしまいました。。。

とりあえずペナルティ分は書けたからよかったけど、週4は無理でした。

そして今気づいたのですが、はてなの暦モードでみると、日曜が週頭になっちゃうんですね。

f:id:kojirooooocks:20180702022151p:plain

なので日曜にブログ書くと次週になっちゃうので、ちょっと嫌だなぁと思っています。

今週から、日〜土の週として考えて、週1ブログを続けてみようと思います。

で、今回は前回の記事の続きで、firebaseのAuthenticationを試してみようと思います。

WEB+DB PRESS Vol.105

WEB+DB PRESS Vol.105

  • 作者: 小笠原みつき,西村公宏,柳佳音,志甫侑紀,池田友洋,木村涼平,?橋優介,大塚雅和,飯塚直,吉川竜太,末永恭正,久保田祐史,浜田真成,穴井宏幸,大島一将,桑原仁雄,牧大輔,池田拓司,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/06/23
  • メディア: 単行本
  • この商品を含むブログを見る

やってみた

はじめのはじめ

公式のドキュメント通りにやってみます。

まずは、プロジェクトフォルダにPodfileを作ります。

pod 'Firebase/Auth'

作成が終わったら、pod installを実行します。

ただ、podコマンドが入ってなかったので、まずcocoapodsをインストールするところからでした。

$ cd test01/
$ gem install cocoapods
...
...
...
Installing ri documentation for cocoapods-1.5.3
Done installing documentation for concurrent-ruby, i18n, thread_safe, tzinfo, activesupport, nap, fuzzy_match, cocoapods-core, claide, cocoapods-deintegrate, cocoapods-downloader, cocoapods-plugins, cocoapods-search, cocoapods-stats, netrc, cocoapods-trunk, cocoapods-try, molinillo, atomos, CFPropertyList, colored2, nanaimo, xcodeproj, escape, fourflusher, gh_inspector, ruby-macho, cocoapods after 17 seconds
28 gems installed

$ pod setup
Setting up CocoaPods master repo
  $ /usr/bin/git clone https://github.com/CocoaPods/Specs.git master --progress
  Cloning into 'master'...
  remote: Counting objects: 2255401, done.        
  remote: Compressing objects: 100% (125/125), done.        
  remote: Total 2255401 (delta 61), reused 35 (delta 35), pack-reused 2255240        
  Receiving objects: 100% (2255401/2255401), 550.08 MiB | 2.19 MiB/s, done.
  Resolving deltas: 100% (1293496/1293496), done.
  Checking out files: 100% (248932/248932), done.
Setup completed

$ pod init
$ cat Podfile 
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'test01' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for test01

end

インストールできました。

作成したPodfileの Pods for test01 の下に pod 'Firebase/Auth' を記載して、 pod installを実行します。

$ pod install
$ pod install
Analyzing dependencies
Downloading dependencies
Installing Firebase (5.4.0)
Installing FirebaseAuth (5.0.2)
Installing FirebaseCore (5.0.5)
Installing GTMSessionFetcher (1.1.15)
Installing GoogleToolboxForMac (2.1.4)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `test01.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 5 total pods installed.

[!] Automatically assigning platform `ios` with version `11.4` on target `test01` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.

なにやら警告が出てるっぽいです。

iosのバージョンの指定が無いから、11.4にセットしときましたよ?って感じだと思います。

このままでもよさそうです。

早速作っていきます。

ちなみに起動するのは、プロジェクト名.xcodeproj ではなく、 プロジェクト名. xcworkspace を起動します。

そして、AppDelegate.swiftに以下を追加します。

f:id:kojirooooocks:20180702022311p:plain

コレで準備万端!

新規登録

さっそく新規登録ボタンに連動するコードを書いていきます!

↓のボタンが押下されたら、FirebaseにAPIを飛ばして、新規ユーザーを追加するような機能を実装します。

f:id:kojirooooocks:20180702022328p:plain

RegisterViewController.swiftに以下のIBActionを追加します。

    import Firebase

    @IBAction func registerButton(_ sender: Any) {
        if (registerMailAddressText.text?.isEmpty == true || registerPasswordText.text?.isEmpty == true) {
            registerErrorText.text = "メールアドレス or パスワード\nが空文字です。確認してください。"
            return
        }
        
        registerErrorText.text = ""
        
        Auth.auth().createUser(withEmail: registerMailAddressText.text!, password: registerPasswordText.text!) { (authResult, error) in
            guard let _ = authResult?.user.email, error == nil else {
                self.registerErrorText.text = error!.localizedDescription
                return
            }

            let storyboard: UIStoryboard = self.storyboard!
            let mainPage = storyboard.instantiateViewController(withIdentifier: "MainPage")
            self.present(mainPage, animated: true, completion: nil)
        }
    }

おそらくFirebase側で空文字登録は弾いてくれると思いますが、とりあえずやってみました。

実際やってみます。

f:id:kojirooooocks:20180702025210g:plain

お、なんかいったっぽい!!!

コンソールも確認します。

f:id:kojirooooocks:20180702023612p:plain

きてるー!!!

ログイン

この調子で、ログインも作ってみます。

LoginViewController.swiftに似たようなコードを作ります。

    import Firebase

    @IBAction func registerButton(_ sender: Any) {
        if (loginMailAddressText.text?.isEmpty == true || loginPasswordText.text?.isEmpty == true) {
            loginErrorText.text = "メールアドレス or パスワード\nが空文字です。確認してください。"
            return
        }
        
        loginErrorText.text = ""
        
        Auth.auth().signIn(withEmail: loginMailAddressText.text!, password: loginPasswordText.text!) { (user, error) in
            if let error = error {
                self.loginErrorText.text = error.localizedDescription
                return
            }
            
            let storyboard: UIStoryboard = self.storyboard!
            let mainPage = storyboard.instantiateViewController(withIdentifier: "MainPage")
            self.present(mainPage, animated: true, completion: nil)
        }
    }

ログインボタンに作成したIBActionを連携させて、早速試してみます!

f:id:kojirooooocks:20180702025959g:plain

できた!!?

終わりに

エラー制御とか、非アクティブ時の制御など、まだまだ考えないといけないことがありますが、とりあえずAuthenticationはOKということで、終わります!

次は、とった写真をCloud Storageにあげるところやってみます。

おやすみなさいー

Firebase試してみた(事前準備編)

はじめに

こんばんは。

今週3記事目行きます。

記事書いていくのは大変なのですが、ネタはいっぱいあるんですよ。

なんてったって積本してるので。

で、今日はこれ。

WEB+DB PRESS Vol.105

WEB+DB PRESS Vol.105

  • 作者: 小笠原みつき,西村公宏,柳佳音,志甫侑紀,池田友洋,木村涼平,?橋優介,大塚雅和,飯塚直,吉川竜太,末永恭正,久保田祐史,浜田真成,穴井宏幸,大島一将,桑原仁雄,牧大輔,池田拓司,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/06/23
  • メディア: 単行本
  • この商品を含むブログを見る

今月号のWeb+Db Pressです。

前回までは先月号だったんですが、すでに今月号も届いてて、コレも例に漏れず積本していたので、消化のためにやっていきます。

今回やりたいのは、今月号で紹介されていたFirebaseを試してみます。

もちろん名前も存在も知ってるんですが、業務でもプライベートでも触ってなかったので、いい機会だということで、触ってみようと思います。

本で紹介されているのは

  • Cloud Messaging
  • Authentication
  • Realtime Database
  • Google Analytics for Firebase

ですが、せっかくなので、前回の記事で作った写真撮影アプリにうまく組み込みもうと思います。

そのため、

  • Authentication
  • Cloud Storage

を使ってみたいと思います。

アプリ開いた時に、ログイン画面を出して、ログインが完了したら、写真撮影。

撮影した写真をStorageにアップできるようにしてみます。

よし!頑張るぞ!

やってみた

はじめのはじめ

さっそくFirebase側の登録をしてみます。

Firebaseのコンソールにアクセスします。

プロジェクトが作られてない場合はプロジェクトを追加してください。

f:id:kojirooooocks:20180701195327p:plain

プロジェクト名を登録して、規約に同意してプロジェクトを作成してください。

f:id:kojirooooocks:20180701195338p:plain

プロジェクトが作成されたらコンソールページに遷移できます。

f:id:kojirooooocks:20180701195354p:plain

Authentication設定

ログイン方法を設定します。

f:id:kojirooooocks:20180701195434p:plain

今回は通常のメール・パスワードでのログインを使おうと思います。

f:id:kojirooooocks:20180701195452p:plain

GoogleService-Info.plistの追加

コンソール画面からアプリを登録します。

f:id:kojirooooocks:20180702005846p:plain

アプリを追加すると、GoogleService-Info.plistがダウンロードできるようになるので、それをプロジェクト内にコピーします。

f:id:kojirooooocks:20180702005917p:plain

これでOK・・・かな?

とりあえず次はアプリ側の修正を行います。

1 or 2枚ページ挟む感じになると思います。

アプリ側設定

前回作成したviewControllerの前にページをはさみます。

こんな感じにしてみました。

f:id:kojirooooocks:20180701195648p:plain

動き的にはこんな感じです。

f:id:kojirooooocks:20180701195703g:plain

Label, TextField, Buttonを使用して、画面を構成しています。

ページ遷移はSegueを使用して設定しました。

この2ページ作るだけでだいぶ時間かかりました。

今ログイン画面のログインボタン、新規登録画面の、新規登録ボタンにはアクションが設定されていません。

次はそれぞれのボタンアクションに、AuthorizationのAPIを実行するようなコードを記載していこうと思います。

終わりに

ページ挟むのに時間がかかりすぎて、体力も時間も消耗したので、記事を分けようと思います。。。

そういえば、何が刺さったのかわからないのですが、前々回の記事が、他の記事に比べてえらく注目されてて、ビビりました。

あの @kakakakku さんの記事と一緒のところにのってて更にビビりました。

すぐにスクショとりました。すいません。

f:id:kojirooooocks:20180701195854p:plain

内容的には相変わらずしょっぱいのですが、見てくれている人もいると思うと、嬉しい半面、頑張って続けないとな。

と、気を引き締めることができました。

続きも書くぞ。

お風呂入ってきます。

iphoneアプリ勉強

はじめに

こんばんは。

連続投稿です。

前回の記事でも書きましたが、こちらの「先月号」のWeb+DB Press.104号で紹介されているものをやってみようと思います。

WEB+DB PRESS Vol.104

WEB+DB PRESS Vol.104

  • 作者: 末田卓巳,林田千瑛,陶山嶺,八谷賢,辰己佳祐,竹澤俊季,服部智,藤岡裕吾,牧大輔,西郡卓矢,松木雅幸,穴井宏幸,新日出海,桑原仁雄,小田知央,ひげぽん,池田拓司,はまちや2,竹原,大場光一郎,大場寧子,松館大輝,日高尚美,Vu Xuan Dung,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/04/24
  • メディア: 単行本
  • この商品を含むブログを見る

今回はiphoneアプリ開発です。

ちなみに自分のiphoneアプリ開発の経験値は

  • Objective-C時代に3ヶ月くらい
  • Swift2時代に1ヶ月くらい

という感じで、アプリ開発に関しては完全ペーペーです。

そしてだいぶ昔にやったことなので、完全に忘れています。

React-NativeとかIonicとかにも興味あるんですが、ネイティブな開発も、知識としては入れておきたいので、少し触っとこうと思います。

今回は、iphoneアプリ開発特集で紹介されている部分を抜粋して、写真撮影とtwitter投稿までをやってみようと思います。

やってみた

はじめのはじめ

とりあえずXcodeを立ち上げて、Create a new Xcode Projectを選択します。

f:id:kojirooooocks:20180630023628p:plain

Xcodeが開いたら、Single View Appが選択されていると思いますので、そのままそれを選択します。

Single View Appは一番シンプルなテンプレートという感じです。

f:id:kojirooooocks:20180630023646p:plain

Choose option for your new projectの画面では、ProjectName Organization Identifier を入力してNextを押します。

  • Use Core Data
  • Include Unit Tests
  • Include UI Tests

は今回使いません。

f:id:kojirooooocks:20180630023707p:plain

こんな感じでXcodeプロジェクトが立ち上がります。

f:id:kojirooooocks:20180630023721p:plain

少しづつ思い出してきた・・・!

写真撮影機能

ImageView設置

storyboardを使って、UIを配置していきます。

カメラで撮影した写真をアプリ上で表示するための場所を作るため、ImageViewを配置します。

f:id:kojirooooocks:20180630023736p:plain

contentModeを設定します。

本では Aspect Fitを設定してますが、コレはなんだろうとちょっと調べると、こちらのサイトで紹介されていました。

Aspect Fit = 縦横の比率はそのままで長い辺を基準に全体を表示する

なるほどなるほど。早速合わせます。

f:id:kojirooooocks:20180630023749p:plain

UIToolbar設置

次はアプリ内で、カメラを起動するボタンと、シェア用のボタンを配置するためUIToolbarを設置します。

こんな感じで画面の下部に設置します。

f:id:kojirooooocks:20180630023800p:plain

デフォルトでボタンが一個付いてるんですが、つけたいのはカメラ起動ボタンとシェアボタンの2つなので、もうひとつツールバーにボタンを設置します。

Button ではなく Bar Button Item なので注意。

f:id:kojirooooocks:20180630023812p:plain

Bar Button Itemドラッグアンドドロップすると、↑の画像のようにデフォルトのボタンの横にくっついてしまいます。

左右にボタンを配置したいので、ボタンとボタンの間に Flexible Spece Bar Button Item を使用します。

f:id:kojirooooocks:20180630023824p:plain

次に配置したボタンをカメラ起動ボタンの方はカメラアイコン、シェアボタンの方はシェア用のアイコンに変更します。

f:id:kojirooooocks:20180630023837p:plain

f:id:kojirooooocks:20180630023849p:plain

こんな感じです。

おーー!アプリっぽいですね。

AutoLayoutで配置を整える

いろんなiphone端末の画面サイズをよしなに対応してもらうためのAutoLayoutを設定します。

本で紹介されている通り、高さの固定が必要なツールバーのAutoLayoutを設定していきます。

f:id:kojirooooocks:20180630023903p:plain

画像のように設定します。

高さを44に固定して、左右と下の余白を0に指定します。



ImageViewは上下左右の余白を0固定にします。

f:id:kojirooooocks:20180630023917p:plain

シミュレーター立ち上げてみます。

f:id:kojirooooocks:20180630023930p:plain

いい感じ!!

カメラ起動

アプリがカメラにアクセスするための許諾設定を行います。

本で紹介されている通り

  • Privacy - Photo Library Additions Usage Description
  • Privacy - Camera Usage Description

を追加します。

f:id:kojirooooocks:20180630023943p:plain

次はカメラ起動用のコードです。 ViewController.swiftに以下のようなコードを記述します。

    @IBAction func didTouchCameraButton(_ sender: Any) {
        let pickerController = UIImagePickerController()
        pickerController.sourceType = .camera
        self.present(pickerController, animated: true)
    }

次にコードとUIを繋ぎ合わせます。

各IBAction、IBOutletの行ラインに丸いアイコンが表示されていると思うので、それをクリックしてドラッグアンドドラックしつつ、各UIに紐づけます。

そうすると以下のような感じになります。

f:id:kojirooooocks:20180630024001p:plain

f:id:kojirooooocks:20180630024013p:plain

f:id:kojirooooocks:20180630024024p:plain

実機を接続してアプリを立ち上げると、カメラを使用できるかどうかの確認が出ます。

f:id:kojirooooocks:20180630024048p:plain

許可すると撮影ができます。

やったー!

プレビューを表示

delegateを設置します。

以下のようなコードにviewController.swiftを変更します。

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { ← 追加
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func didTouchCameraButton(_ sender: Any) {
        let pickerController = UIImagePickerController()
        pickerController.sourceType = .camera
        pickerController.delegate   = self                 ← 追加
        self.present(pickerController, animated: true)
    }
    
    @IBAction func didTouchShareButton(_ sender: Any) {
    }

    // ↓ 追加
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            imageView.image = image
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
        }
        picker.dismiss(animated: true, completion: nil)
    }
}

f:id:kojirooooocks:20180630024211p:plain

でました!!

Twitter投稿

次は、シェアボタンの機能を作成します。

UIActivityという機能を使うようです。

こんな感じで追加します。

    @IBAction func didTouchShareButton(_ sender: Any) {
        guard let imageData = UIImageJPEGRepresentation(imageView.image!, 1.0) else {
            return
        }

        let activityController = UIActivityViewController(activityItems: [imageData], applicationActivities: nil)
        self.present(activityController, animated: true, completion: nil)
    }

起動させてみます。

f:id:kojirooooocks:20180630024238p:plain

f:id:kojirooooocks:20180630024322p:plain

できたー!!!!

終わりに

写真撮ってTwitter投稿するだけのアプリでしたけど、本に沿ってやれば簡単にできました。

本では更に写真加工をしたりもするので、さらにいろいろとできそうです。

やっぱアプリ面白いなぁー。

お疲れ様でした。

Pythonのwebフレームワーク試してみた

はじめに

こんばんは。久々投稿です。

最近仕事もプライベートも忙しくて、全くブログ書く時間取れませんでした。

先週はかけてないので、今週は意地でも4記事書きます。

幸い先々月からの積本がいっぱいあるので、それを消化できるかなと。

で、今回はこれ。

WEB+DB PRESS Vol.104

WEB+DB PRESS Vol.104

  • 作者: 末田卓巳,林田千瑛,陶山嶺,八谷賢,辰己佳祐,竹澤俊季,服部智,藤岡裕吾,牧大輔,西郡卓矢,松木雅幸,穴井宏幸,新日出海,桑原仁雄,小田知央,ひげぽん,池田拓司,はまちや2,竹原,大場光一郎,大場寧子,松館大輝,日高尚美,Vu Xuan Dung,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/04/24
  • メディア: 単行本
  • この商品を含むブログを見る

そう。「先月号」のWEB+DB PRESSpython入門です。

ずーっと積んでて、全く消化のタイミングがなかったので、今回やろうと思いました。

pythonはポコポコ小さいツールを作るくらいしか触ったことなかったので、はやりの機械学習や、FWを使用したweb開発まで書かれてて、触りにはもってこいかなと。

で、今回は、本誌でチュートリアルまで紹介されている Bottle と、名前だけ紹介されている Flask Tornado Django を試してみようと思います。

では。さっそくやってみます。

やってみた

Bottle

軽量フレームワークとして紹介されてます。

FWのコアファイルは1ファイルのみで構成されてるみたいです。

インストール

$ mkdir bottle_test
$ cd bottle_test/
$ python3 -m venv bottle_test
$ source bottle_test/bin/activate
(bottle_test)$ pip install bottle
Collecting bottle
  Downloading https://files.pythonhosted.org/packages/bd/99/04dc59ced52a8261ee0f965a8968717a255ea84a36013e527944dbf3468c/bottle-0.12.13.tar.gz (70kB)
    100% |████████████████████████████████| 71kB 2.0MB/s 
Installing collected packages: bottle
  Running setup.py install for bottle ... done
Successfully installed bottle-0.12.13
(bottle_test)$ bottle.py --version
Bottle 0.12.13

ページ作成

index.pyを作成します。

from bottle import route, run

@route('/')
def index():
    return 'Hello Bottle!'

@route('/about')
def about():
    return 'About Page!'

run(host='localhost', port=1234, reloader=True)

起動

(bottle_test)$ python index.py 
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://localhost:1234/

ブラウザで表示された、 http://localhost:1234/ にアクセスすると、以下のような感じで表示されます。

f:id:kojirooooocks:20180629024015p:plain

ついでに作ったaboutページもこんなかんじで。

f:id:kojirooooocks:20180629024028p:plain

@route の第二引数にmethodを指定でき、POST通信指定などができます。

@route('/save', method='POST')
def save():
    return 'SavePage'

Post通信じゃないとこの通り

f:id:kojirooooocks:20180629024038p:plain

Rest通信ツールとかでPOST通信させるとちゃんと通ります。

f:id:kojirooooocks:20180629024049p:plain

テンプレートエンジン使ってみる

Bottleには、SimpleTemplate っていうテンプレートエンジンを使ってるようです。

早速やってみます。

views/base.tpl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Bottle Test</title>
    <link rel="stylesheet" href="https://cdn.rawgit.com/Chalarangelo/mini.css/v3.0.0/dist/mini-default.min.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <header>
        <a href="/" class="button">Bottle Test</a>
    </header>
    {{ !base }}
</body>
</html>

views/index.tpl

% rebase('views/base.tpl')

<div>
    <p>はじめましてKojirockです</p>
    <p>音楽大好きです。よろしくね。</p>

    <fieldset>
        <legend align="left">お問い合わせ</legend>

        <div>
            <div><label for="name-form">お名前</label></div>
            <input type="text" id="name-form" name="name" placeholder="こじろっく" />
        </div>

        <div>
            <div><label for="mail-form">メールアドレス</label></div>
            <input type="email" id="mail-form" name="mail_address" placeholder="kojirock@kojikoji.com" />
        </div>

        <div>
            <div><label for="tel-form">電話番号</label></div>
            <input type="tel" id="tel-form" name="tel" placeholder="09011111111" />
        </div>

        <button>問い合わせる</button>
    </fieldset>
</div>

index.py

from bottle import route, run, template

@route('/')
def index():
    return template('views/index.tpl')

run(host='localhost', port=1234, reloader=True)

アクセスするとこんな感じです。

f:id:kojirooooocks:20180629024104p:plain

次行ってみます。

Flask

こちらも軽量フレームワークとしてうってるフレームワークらしいです。

拡張機能が多いようで、Bottleよりも大規模向きって感じ?みたいです。

インストール

$ mkdir bottle_test
$ cd flask_test/
$ python3 -m venv flask_test
$ source flask_test/bin/activate
(flask_test)$ pip install flask
Collecting flask
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
    100% |████████████████████████████████| 92kB 2.3MB/s 
Collecting Werkzeug>=0.14 (from flask)
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
    100% |████████████████████████████████| 327kB 2.3MB/s 
Collecting itsdangerous>=0.24 (from flask)
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
    100% |████████████████████████████████| 51kB 4.8MB/s 
Collecting click>=5.1 (from flask)
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
    100% |████████████████████████████████| 71kB 2.3MB/s 
Collecting Jinja2>=2.10 (from flask)
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
    100% |████████████████████████████████| 133kB 575kB/s 
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask)
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Installing collected packages: Werkzeug, itsdangerous, click, MarkupSafe, Jinja2, flask
  Running setup.py install for itsdangerous ... done
  Running setup.py install for MarkupSafe ... done
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24
(flask_test)$ flask --version
Flask 1.0.2

ページ作成

index.pyを作成します。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello Flask!'

if __name__ == '__main__':
    app.env = 'development'
    app.run(host='0.0.0.0', port=1234)

起動

(flask_test)$ python index.py 
 * Serving Flask app "index" (lazy loading)
 * Environment: development
 * Debug mode: off
 * Running on http://0.0.0.0:1234/ (Press CTRL+C to quit)

ブラウザで表示された、 http://localhost:1234/ にアクセスすると、以下のような感じで表示されます。

f:id:kojirooooocks:20180629024121p:plain

envを指定できたり、debugモードがあったり、結構多彩なことができるみたいです。

routing周りはBottleとほぼほぼ一緒でした。

テンプレートエンジン使ってみる

FlaskにはJinja2というテンプレートエンジンが備わってるので、早速使ってみます。

templates/base.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Bottle Test</title>
    <link rel="stylesheet" href="https://cdn.rawgit.com/Chalarangelo/mini.css/v3.0.0/dist/mini-default.min.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <header>
        <a href="/" class="button">Bottle Test</a>
    </header>

    {% block content %}{% endblock %}
</body>
</html>

templates/index.html

{% extends "base.html" %}
{% block content %}
<div>
    <p>はじめましてKojirockです</p>
    <p>音楽大好きです。よろしくね。</p>

    <fieldset>
        <legend align="left">お問い合わせ</legend>

        <div>
            <div><label for="name-form">お名前</label></div>
            <input type="text" id="name-form" name="name" placeholder="こじろっく" />
        </div>

        <div>
            <div><label for="mail-form">メールアドレス</label></div>
            <input type="email" id="mail-form" name="mail_address" placeholder="kojirock@kojikoji.com" />
        </div>

        <div>
            <div><label for="tel-form">電話番号</label></div>
            <input type="tel" id="tel-form" name="tel" placeholder="09011111111" />
        </div>

        <button>問い合わせる</button>
    </fieldset>
</div>
{% endblock %}

index.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
     return render_template('index.html');

if __name__ == '__main__':
    app.env = 'development'
    app.run(host='0.0.0.0', port=1234)

f:id:kojirooooocks:20180629024150p:plain

twigっぽくていいですね。

あとテンプレートファイルは templates ディレクトリ以下に置くと、自動で認識してくれるみたいです。

あとhtmlファイルのままってのも、コーダーさんは触りやすそう。

Tornado

非同期処理が得意なフレームワークらしいですね。

とにかく名前がかっこいいです。

インストール

$ mkdir tornado_test
$ cd tornado_test/
$ python3 -m venv tornado_test
$ source tornado_test/bin/activate
(tornado_test)$ pip install tornado
Collecting tornado
  Downloading https://files.pythonhosted.org/packages/cf/d1/3be271ae5eba9fb59df63c9891fdc7d8044b999e8ac145994cdbfd2ae66a/tornado-5.0.2.tar.gz (506kB)
    100% |████████████████████████████████| 512kB 2.2MB/s 
Installing collected packages: tornado
  Running setup.py install for tornado ... done
Successfully installed tornado-5.0.2

ページ作成

公式githubに書いてあったコードそのまま持ってきました。

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, Tornado")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(1234)
    tornado.ioloop.IOLoop.current().start()

f:id:kojirooooocks:20180629024207p:plain

表示されました。

テンプレート表示してみる

import os
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ],template_path=os.path.join(os.getcwd(), 'templates'))

if __name__ == "__main__":
    app = make_app()
    app.listen(1234)
    tornado.ioloop.IOLoop.current().start()

テンプレートは同じです。

f:id:kojirooooocks:20180629024218p:plain

簡単でした。

Django

あんまpython触ったことない自分でも知ってる、ど定番のフルスタックフレームワークです。

CakephpとかRailsとかの立ち位置かな??

インストール

$ mkdir bottle_test
$ cd flask_test/
$ python3 -m venv django_test
$ source django_test/bin/activate
(django_test)$ pip install django
Collecting django
  Downloading https://files.pythonhosted.org/packages/56/0e/afdacb47503b805f3ed213fe732bff05254c8befaa034bbada580be8a0ac/Django-2.0.6-py3-none-any.whl (7.1MB)
    100% |████████████████████████████████| 7.1MB 239kB/s 
Collecting pytz (from django)
  Downloading https://files.pythonhosted.org/packages/dc/83/15f7833b70d3e067ca91467ca245bae0f6fe56ddc7451aa0dc5606b120f2/pytz-2018.4-py2.py3-none-any.whl (510kB)
    100% |████████████████████████████████| 512kB 2.2MB/s 
Installing collected packages: pytz, django
Successfully installed django-2.0.6 pytz-2018.4

(django_test)$ python
Python 3.6.4 (default, Jun 29 2018, 00:24:39) 
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.get_version()
'2.0.6'

ページ作成

フルスタックフレームワークっぽい、プロジェクト作成コマンドがありました。

(django_test)$ django-admin startproject test_project
(django_test)$ tree test_project/
test_project/
├── manage.py
└── test_project
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

1 directory, 5 files

起動

(django_test)$ python test_project/manage.py runserver 1234
Performing system checks...

System check identified no issues (0 silenced).

You have 14 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

June 28, 2018 - 17:12:22
Django version 2.0.6, using settings 'test_project.settings'
Starting development server at http://127.0.0.1:1234/
Quit the server with CONTROL-C.

マイグレーションしろみたいなエラーが出ますが、とりあえず無視しときます。

f:id:kojirooooocks:20180629024237p:plain

でました。かっこいいな。

とりあえずDjangoはここまで。

終わりに

実際にweb開発で使いそうなのは、やっぱDjangoくらいのFWになりそうな気もしてます。

マジで触りなので、Djangoはもうちょい触ってみようと思いました。

スタートページだけなので、viewファイル追加したり、dbアクセスしたりなど、次はそのヘンもうちょいやってみようと思います。

お疲れ様でした。