雑食日誌

Vue.jsとServerless。ときどきチーム開発

Django備忘録: 管理画面のパスワードリセットにメールを使う

Djangoの管理画面のパスワードリセットにメールアドレス認証を実装する機会があったので、記事にまとめてみました。 公式ドキュメントを参考に実装しています。

Django の admin サイト | Django documentation | Django

GitHubに実装を公開しています。

github.com

目次

Djangoの初期設定

Djangoのマネージドコマンドを使ってプロジェクトの初期化を行います。

$ django-admin startproject django_admin_email

URLの追加

Djangoは管理画面のパスワードリセットのURL設定を追記するだけで設定できます。
URLの name はテンプレートで決められているため、変えられません。
urls.pyに以下を追記する。

from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path

urlpatterns = [
   path('admin/', admin.site.urls),
   path(
       'admin/password_reset/',
       auth_views.PasswordResetView.as_view(),
       name='admin_password_reset',
   ),
   path(
       'admin/password_reset/done/',
       auth_views.PasswordResetDoneView.as_view(),
       name='password_reset_done',
   ),
   path(
       'reset/<uidb64>/<token>/',
       auth_views.PasswordResetConfirmView.as_view(),
       name='password_reset_confirm',
   ),
   path(
       'reset/done/',
       auth_views.PasswordResetCompleteView.as_view(),
       name='password_reset_complete',
   ),
]

/admin/login にアクセスするとログイン画面にパスワードリセットのリンクが追加されます。

f:id:keinumata:20181119002406p:plain

管理画面のログインのテンプレートHTMLを確認してみましょう。
password_reset_url がURLに登録されているかで、リンクを出し分けていることがわかります。

django/login.html at master · django/django · GitHub

  {% if password_reset_url %}
  <div class="password-reset-link">
    <a href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a>
  </div>
  {% endif %}

Gmailで送ってみる

開発環境でのメール送信はc-bataさんの記事が参考にになります。

ここではGmailを使ったメール送信を設定してみます。 settings.pyに以下の設定を追加します。 EMAIL_HOST_USER , EMAIL_HOST_PASSWORD は適宜変更してください。

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'example@gmail.com'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_PORT = '587'

送信する前にGoogle側のパスワード設定を変える必要があります。
アカウント設定の「ログインとセキュリティ」から「 安全性の低いアプリの許可」をONにします。

実際に送られたメールはこちら。

このメールは 0.0.0.0:8000 で、あなたのアカウントのパスワードリセットが要求されたため、送信されました。

次のページで新しいパスワードを選んでください:

http://0.0.0.0:8000/reset/MQ/xxxxxxxxxxxxxxxxx/

あなたのユーザー名 (念のため): keinuma

ご利用ありがとうございました!

 0.0.0.0:8000 チーム

ローカル感丸出しですが無事に送れました。

SendGridやAWS SESを組み込んだメール送信もやってみたいです。

Vue Fes Japan参加レポート

日本初のVueカンファレンスVue Fes Japanに参加してきました。 実業務でのお話やVueのこれからのようなコアな話まで聞けたので、レポートにまとめたいと思います。

キーノート

Evan氏によるVue3.0の展望についてでした。 主に以下の変更が加えられます。

  • メモリ使用量低下による高速化
  • ビルドファイルの軽量化
  • メンテナンスしやすさの向上
  • Hooks API

何点か気になった話を書いていきます。
ビルドファイルの軽量化ではTree Shakingが出てきました。 使われていない v-ifv-model などをバンドルせずにビルドできるそうです。

メンテナンスのしやすさではデバッグ機能の追加がありました。
Devtoolの確認に加えて、イベントなどのブレークポイントを見れるようになるのはでかいですね。

Hooks APIはReactで出てきた機能らしいです。アフターパーティーで詳しく聞けました。
ストア管理するほどでもない状態を楽にできるそうなので、Reactを触って先に体験したいですね。

勉強不足で多くを理解できなかったですが、復習して3.0を待ちたいと思います。

ランチセッション

LINEさん

ランチセッション一発目はLINEさんでした アプリだけではなく、クーポンやスタンプのページはVueで開発されているようです。 新規開発もNuxtやVueを用いて開発されてるみたいですねー。

ブースではVueのクイズに答えると抽選でVue.js入門をゲットできる催しをやってました。 抽選は逃してしまいましたが、面白い企画でした。

Scouterさん

NuxtMeetUpでおなじみのScouterさんの発表でした。 みなさんだいたい2014年のVueがメジャーバージョンになる前から使い始めてますね。 Scouterさんはエンジニア10名で継続的にプロダクトをローンチしていてすごいです。

Reproさん

ラストは普段もくもく会でお世話になっているReproさんのセッションでした。 最初はjQueryで開発されていたものをVueで書き換えたということでした。 jQueryからの移行しやすさはVueの魅力の一つなのかなと思いました。

セッション

Vue.js と Web Components のこれから

speakerdeck.com

WebComponents(以下WC)とVueとの共存についてのセッションでした。 WCのメリットは以下のようです。

デメリットは以下

  • 属性値にstringしか渡せない
  • 外部からのイベントハンドリングが難しい
  • CSS設計方法が大きく変わる

Vue CLI3はWCへのbuildをサポートしているためビルド時にtargetオプションをつけることで動かせます。(参考)

$ vue-cli-service build --target wc --name element ./src/App.vue

そのため、Vueで開発してきたものをWCを用いて描画することが簡単にできます。

WCはWeb標準機能のため、相対的に寿命が長く、激しい変更が少ないことが見込まれます。 いつVueが終わってしまうかという心配事が減ることが期待できますね。

WCはあくまでDOMの可視化にまつわる仕組みなのでアプリケーションフレームワークとしての機能は有していません。
データ管理やイベントなどフレームワークとしてはVueが、UI部分はWCに置き換わるのが今後の展望とのことでした。
WCのデフォルトでマテリアルデザインをサポートし始めたら強いですね。

記法がVueに似ている(VueがWCに着想を得ている)とのことだったので自作アプリにも組み込みたいです。

Vue Designer: デザインと実装の統合

slides.com

デザインと実装に距離ができてしまう問題を解決するツールについてのセッションでした。
デザインとフロントエンドの実装では以下の課題がみられます。

  • デザインファイルと実装で使うツールが異なる
  • デザインと実装の管理が重複する
  • 動的なデザインを考慮しにくい

現在開発されていて、上記課題を解決しようとしているツールには以下があるそうです。

  • vuegg
    • デザインしたものをVueファイルとして出力できる
    • コード上で改修したものをキャンバスに戻せない
  • framer X

そこでkatashinさんはSFCでデザインできるようにするツールを開発中でした。
GitHub - ktsn/vue-designer: Vue component design tool

SFCをプレビューし、ドラッグ&ドロップやインスペクターでデザインを変更します。
変更はSFCのファイルにも反映されて、デザインとSFCファイルの双方向バインディングが実現されていました。
デザイナーにも使ってもらえることを目標にしているので、デザインツールで使えるような機能も考えているようです。

実装はクライアント・サーバ型でできていて、SFCをASTに変換し、WebSocketを通してレンダリングされていました。
クライアント・サーバ型のメリットは以下になるそうです。

パースはvue-eslint-parser, postcss, babelを使っていて、TypeScript対応もあるみたいですね。

一人でパーサーを書いて、WebSocketを使い、デザインツールを追従していることが驚きでした。

今後はコンポーネントカタログの自動生成も考えているそうなので、とても期待しています。

Atomic Design のデザインと実装の狭間

デザイナーとエンジニア間の協業という観点のエモいお話でした。

現在Atomic Designが主流になる中でデザイナーからコンポーネントベースの手法が適応されている背景があります。 コンポーネントベースでデザインする上での難点は以下になっているそうです。

  • 学習コストが高い
  • デザインの自由度が下がる
  • 認識が異なる

コンポーネントはエンジニアの概念のため、デザイナーに適応することは難しいそうです。
例として挙げられていたのは構造化プログラミング vs オブジェクト指向プログラミングでした。
Javaが出てきた当初はスピードなどを槍玉に挙げられて非難されていたが、オブジェクト指向に慣れると、 効率が上がるため現在では主流になっています。
デザインの世界では構造化プログラミング的手法のため、いきなり実践するということは無理とのことです。

そもそもデザイナーとエンジニアは責任が異なります。
デザイナーはインターフェースの価値を高めて、ユーザーに届け、エンジニアは効率を重視し、保守性を高めています。

そんな中での現実的な解決策は以下が挙げられていました。

  • エンジニアがデザインを覚える
  • いい感じのツールを作る(さっきのトークの続きだ…)
  • デザイナーの横でコンポーネントをひたすら写経する

弊社ではエンジニアがコンポーネント化する作業を行なっているので、より良いプロセスを考えていきたいです。

noteをNuxt.jsで再構築した話

speakerdeck.com

SPAからNuxtへ移行したためになるお話でした。

noteは当初Angularで開発されていたようです。
この時の技術選定はAngularのみメジャーバージョンに上がっていることが大きかったようです。

しかし、SPAのみでは以下の課題がありました。

  • 初期表示速度の遅延
  • コーディング規約などの負債

フレームワークを変えてのフルスクラッチは難しいと思います。
noteさんはCEOが技術よりということで理解が得られていました。

Vueを選んだ理由は以下になるそうです。

  • 学習コストの低さ
  • ドキュメントの充実度(日本語が充実)
  • コミュニティの活発度が高い

移行手順はページごとに社内リリースから初めて徐々に本番に移行していくやり方ですね。

  • 既存UIを確実に移行
  • ページごとにURLベースで振り分ける
    • 社内リリース、Dog Fooding、一般公開の順番

設計方針ではVuex + Atomic Designを取り入れていました。
Vueを使って堅牢なシステムを作るときのベストなのかなと思います。

個人的にAtomic Designは分子と有機体の違いがわからなくなりますね…
Vuexの参照は有機体以上にしているそうです。
ルールを明文化して、コードレビューの観点にすることが大事なんだと思いました。

Atomic Designを用いることでコンポーネント数が肥大化し、チームが把握できない問題を解決するためにStory Bookを導入していました。
私自身設定周りで挫折した経験があるのですが、Nuxt2.0になったことで楽になっているそうです。
ストーリーをメンテナンスするのが大変なのと、通信周りのスタブ化が辛いため、対象を原子・分子に絞っていました。

インフラはNuxt on Lambdaの構成でした。
ハマりポイントは mya-akeさんのブログで解消できるそうです。

インフラ管理のコストやスケーリングを考えると良さそうですね。
以下の欠点を超えられれば使いたいです。

  • NodeのバージョンがLambdaに依存する
  • デプロイできる容量が決まっている

これまでの移行プロジェクトはnoteのなかでまとまっています。 note.mu

Nuxtの導入を考えているのでとても参考になりました。

1年間単体テストを書き続けた現場から送る Vue Component のテスト

speakerdeck.com

ラストはコンポーネントテストのお話でした。
コンポーネントの何をどこまでテストするかはいつも悩むのでノウハウを聞けてよかったです。

単体テストを自動化するのは外部からみた振る舞いをテストしていました。
「外部からみた振る舞い」は何かしらのインプットに対してアウトプットが期待とおりかチェックすることです。

コンポーネントの場合のインプットは

これに対してアウトプットは

  • HTML, CSS
  • Event
  • Storeのアクション

にまとめられていました。

Lifecycleでは必要なデータを取得するぐらいなので優先度は低いそうです。
他のテストに影響が出るので、場合によってはモックしてもよさそうですね。

PropsやStoreはスナップショットテストを用いていました。
コード修正の前後の差分を確認し、期待通りであればUpdateする機能です。
今まで使いこなせていない機能なので活用したいと思います。

文字列だけではなくCSSのテストをVisual Testにて行なっていました。
スナップショットテストの応用で画像を取得して差分を出力する仕組みです。
CIに組み込むことで、レビュー観点の一つになるため、レビュワーの負担が軽減するのが良さげです。
レビュワーがチェックアウトしてビルド・確認をする手間が省けるのは大きいですね。

ツールはStorybookreg-suitoを用いていました。

User Interfaceのテストも同様にVisual Testを使っていました。
テスト範囲はクリックとinputイベントに絞っていて、難しいテストは実施しない選択を取っているそうです。

変更に強くするためのテストであることを忘れずに自分のプロジェクトに適応していきたいです。

まとめ

フロントエンドのカンファレンス自体初めてでしたがとても内容の濃い1日でした。
実践的なお話をたくさん聞けたので、どんどん投入していきたいと思います。
スタッフの方々本当にありがとうございました。

GraphQLを試してみた

こんにちは。ぬまたです。
約半年ぶりの投稿です。空けすぎました。
最近AppSyncを触る機会があったので、基礎となるGraphQLを学びたいと思います。

目次

GraphQLとは

GraphQLはフロントエンドがサーバのデータをいい感じに取得するためのクエリ言語です(怒られそう)。 ざっと調べる限りは以下のような特徴がありました。

  • 読み取りと書き込みが分離している
  • エンドポイントが一つ
  • データ群の処理結果などをパラメータに追加できる
  • Schemaや型を定義する
  • 変数やコメントアウトを使うことができる
  • キャッシュが比較的容易

実際に使ってみないとわからないので、GitHub APIで試してみます。 GitHubAPIのバージョン4からGraphQLを導入しています。

参考: - GitHub API v4

検証環境

GitHubのデベロッパーツールを使用します。

GraphQLはエンドポイントをリソースごとに分けずにパラメータで指定します。 GitHubではリクエストを https://api.github.com/graphql に向け、パラメータにクエリを入れています。

GitHub API を試してみる

ログインしているユーザーのメールアドレス取得

単一のリソースfieldsから指定したキーの値を取得できます。 現在ログインしているユーザのメールアドレスを取得してみます。

GitHubviewer という fieldsにログインユーザー情報が入っています。 クエリ内にパラメータ(email)を指定して取得することができます。

クエリ

{
 viewer {
   email
 }
}

結果

{
 "data": {
   "viewer": {
     "email": "nununu.mono@gmail.com"
   }
 }
}

Dockerの関連トピックを取得

fields は引数を取ることができます。 GitHubのDockerトピックに関連するトピックを取得してみます。

クエリ

query {
 topic(name: "docker") {
   relatedTopics {
     name
   }
 }
}

結果

{
 "data": {
   "topic": {
     "relatedTopics": [
       {
         "name": "docker-container"
       },
       {
         "name": "jhipster"
       },
       {
         "name": "docker-image"
       },
       {
         "name": "database"
       },
       {
         "name": "ruby"
       },
       {
         "name": "composer"
       },
       {
         "name": "nginx"
       },
       {
         "name": "yii"
       }
     ]
   }
 }
}

relatedTopics はオブジェクトのリストになっています。 ネストしたオブジェクトのキー(今回は name )を選択できることが異なる点ですね。

リポジトリ内のPR一覧を取得

nodes を使ってリスト値と欲しいパラメータを取得することができます。 今回はよく使うDjangoRESTFrameworkのPR一覧とそれぞれのコミット数を調べてみます。

クエリ

query { 
 repository(owner: "encode" name: "django-rest-framework") { 
   pullRequests(last: 5 states: OPEN) {
     nodes {
       number
       title
       commits {
         totalCount
       }
     }
   }
 }
}

結果

{
 "data": {
   "repository": {
     "pullRequests": {
       "nodes": [
         {
           "number": 6279,
           "title": "Let SearchFilter subclasses dynamically set search fields",
           "commits": {
             "totalCount": 4
           }
         },
         {
           "number": 6282,
           "title": "Ensure serializer and field validators support both lists and tuples",
           "commits": {
             "totalCount": 2
           }
         },
         {
           "number": 6284,
           "title": "Don't force implement get_queryset",
           "commits": {
             "totalCount": 1
           }
         },
         {
           "number": 6286,
           "title": "permissions must return a boolean to allow &/| operator comparison",
           "commits": {
             "totalCount": 3
           }
         },
         {
           "number": 6288,
           "title": "Fix DjangoObjectPermissionsFilter deprecation note",
           "commits": {
             "totalCount": 2
           }
         }
       ]
     }
   }
 }
}

pullRequests の引数にPRが OPEN であることと、最新の5つを取得するよう指定しています。 DjangoRESTFrameworkのGitHubをみると正しそうです。

nodes はPRのリスト値にあたり、その中でPRのNoやコミット数を指定しています。 commitstotalCount パラメータが入っているので、データを全て取得せずに総数を把握することができますね。 自分でSchemaを定義する場合はクライアントが欲しい値を自由に追加することができます。

特定条件のリポジトリ数の取得

GraphQLのPaginationを使ってクエリ結果の総数を取得できます。 Pythonで書かれているスター数1000以上のリポジトリが何個あるか調べてみます。

クエリ

query {
 search(query: "language: Python stars:>1000", type: REPOSITORY) {
   repositoryCount
}

結果

{
 "data": {
   "search": {
     "repositoryCount": 28
   }
 }
}

searchGitHubで定義されているコネクションです。クエリに加えて読み取り開始位置 after などを定義できるようになっています。

今回は querytype に検索したい条件をして、値に repositoryCount を取ることでリポジトリ数を取得しています。 search の引数を変えて、色々な検索をかけられるのはよいですね。

まとめ

今回は読み取りの Query を中心にGraphQLを試してみました。 GraphQLのメリットはクライアントの負担が減ることなのかなという所感です。

Schemaの定義はクライアントの要求を取り入れつつ、データベース内のモデルの知識が求められそうですね。 その辺りも今後調べたいと思います。

BacklogAPIライブラリを作ってみた

Pythonエンジニア見習いのぬまやんです。

今回はBacklogAPIのクライアントライブラリを作ってみました。
Backlogの課題やプロジェクトをオブジェクトとして扱えるようにしています。

目次

動機

今まで作りかけだった自作のライブラリにエキスパートPythonプログラミングの内容を実践してみたいと思ったことがきっかけです。
エキスパートPythonプログラミングは言語の基本文法だけでなく、パッケージの作り方やテスト、プロジェクトの管理 についてPythonのベストプラクティスをまとめていただいている本になります。

エキスパートPythonプログラミング改訂2版

エキスパートPythonプログラミング改訂2版

作ったもの

作成したライブラリはGitHubにて管理していて、PyPIに公開しています。

pypi.org

使い方

実現したかったことはBacklogのプロジェクトや課題をオブジェクトとして扱うことです。
そのため以下のような操作感にしています。

from backlogapi import BacklogClient

client = BacklogClient(api_key='apiKey', space_name='spaceName'
# Space内の全プロジェクトを取得する
projects = client.project.all()
project1 = projects[0]

# プロジェクト内の課題を取得する
issues = project1.get_issues()

# 取得した課題を削除する
for issue in issues:
    issue.delete()

エキスパートPythonプログラミングを参考にしたところ

PyPIのへのアップロード

今回のライブラリの目標はPyPIにアップロードして、公開することでした。 公開するにはsetup.py, MANIFEST.in, requirements.txtなど準備するものが多いです。
本書にはこの設定方法について詳細や一般的な手段が記されています。

本書内で何箇所か説明されている新しいPyPIはすでに公開されています。 pypi.org
さらに Project DescriptionについてMarkdownの対応も完了しています。 対応方法はsetup.pyのsetup関数に以下を追加するだけです。

long_description_content_type='text/markdown',

コード管理手法

コードの管理として、継続的デプロイが紹介されています。
私はGitHubにてホスティングしていたため、TravisCIを用いて自動テストを行いました。 TravisCIを用いた所感は以下になります。

  • 新規の仮想環境にてテストを行うことができる
  • GitHubとの連携が簡単
  • 設定方法のドキュメントが読みやすい

他にも紹介されているJenkinsやBuildbotについても試していきたいと思います。

テスト駆動開発

テストについては基礎から応用まで幅広く記されています。 具体的にはunittestを用いた基本的なテスト方法から拡張モジュールであるnose, pytestの使い方が説明されています。 今回はBacklogという外部APIを用いたライブラリなので、Mockを作成する方法やtoxを使ったマトリックステストも使ってみました。

本書から引用したツール・ライブラリは以下になります。

作ってみた感想

ベースとなる書籍があったためPyPIでの公開はスムーズにできました。
しかし、ライブラリ自体を使用する側の目線に立って作ることが難しかったです。
現状はまだまだ使いにくいものなので、改善していきたいと思います。 また、使用感を教えていただけると大変嬉しいです。

line-bot-sdk-pythonを使ってみた

はじめに

line-bot-sdk-pythonPythonを用いてLINE botを作りました。 ライブラリの操作で何箇所か詰まってしまったので、備忘録として記載します。

開発環境

ライブラリ

概要

line-bot-sdk-pythonはLINEのMessaging APIのクライアントライブラリであり、Bot開発を簡単に始めることがきます。 デコレータを用いて、イベントごとの処理を分けて記述したり、Signatureから署名を検証する操作を簡単にかけます。

事前準備

初めてLINE Botを作成するときは以下の公式ドキュメントからチャネルの作成まで完了する必要があります。 登録が終わったら、自分のBotの各種設定をコンソール画面から取得することができます。

URL設定

LINEのWebhookを用いるとBotへのメッセージや友達追加などのイベント時にHTTP POSTリクエストを発行できます。 コンソール画面のWebhook URLの項目にPOST先のURLを定義することで設定が完了します。

Djangoではルーティングを用いてURLを設定します。

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('callback', views.callback)
]

認証

LINE Messaging APIを用いいる場合は以下の二つの定義が必要になります。

  • LINE_ACCESS_SECRET: 署名の検証で使用
  • CHANNEL_ACCESS_TOKEN: 各API通信を行うときに使用

コンソール画面のChannel基本設定から、Channel Secretとアクセストークン を発行します。これらがそれぞれLINE_ACCESS_SECRETとCHANNEL_ACCESS_TOKENに当たります。

Djangoのviews.py上では以下になります。

views.py

from linebot import LineBotApi, WebhookHandler

# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)

handlerの使い方

レスポンスの関数呼び出しとリクエストの署名検証

リクエストごとの関数を呼び出すためにはWebhookHandlerのhandleメソッドを用います。 handleメソッドは署名検証も同時に行います。 引数にはレスポンスのbodyと署名検証のためのsignatureを使います。

views.py

from django.http import HttpResponseForbidden, HttpResponse
from linebot.exceptions import InvalidSignatureError

def callback(request):
    # リクエストヘッダーから署名検証のための値を取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    # リクエストボディを取得
    body = request.body.decode('utf-8')
    try:
        # 署名を検証し、問題なければhandleに定義されている関数を呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        # 署名検証で失敗したときは例外をあげる
        HttpResponseForbidden()
    # handleの処理を終えればOK
    return HttpResponse('OK', status=200)

イベントごとに関数を定義

handlerのaddメソッドを用いて、リクエストのイベントごとに実行する関数を記述できます。 例えば、友達追加されたときに「初めまして」というメッセージを送信する処理は以下のようになります。

views.py

# linebot.modelsから処理したいイベントをimport
from linebot.models import (
    FollowEvent, TextSendMessage
)

# addメソッドの引数にはイベントのモデルを入れる
# 関数名は自由
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )

handler.addメソッドには引数にリクエストのイベントを指定します。 イベントはlinebot.modelsに定義されていて、メッセージやグループ参加などがあります。 関数の引数であるeventはLINE Messaging APIで定義されているリクエストボディになります。

リプライはLineBotApiのメソッドを用いています。 第一引数のevent.reply_tokenはイベントの応答に用いるトークンです。 第二引数にはlinebot.modelsに定義されている返信用のTextSendMessageオブジェクトを渡しています。

handler.addメソッドはメッセージイベントの場合にテキストや画像のようなメッセージの 内容でも処理を分けることができます。

from linebot.models import (
    MessageEvent,
    TextMessage ,ImageMessage, AudioMessage
)

# メッセージイベントの場合の処理
# かつテキストメッセージの場合
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )


# 画像、音声メッセージの場合
@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

まとめ

line-bot-sdk-pythonを使ってイベントごとにレスポンスを変える処理を作りました。 処理部分を関数で簡単に分けることができるためとてもシンプルに書くことができます。 この処理を使って複数のAPIを連携したbotを作ったので、後日まとめたいです。

コード例

views.py

from django.http import HttpResponseForbidden, HttpResponse

from linebot.exceptions import InvalidSignatureError
from linebot.models import (
    MessageEvent, TextMessage, FollowEvent, UnfollowEvent,
    TextSendMessage, ImageMessage, AudioMessage
)

from linebot import LineBotApi, WebhookHandler


# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)


def callback(request):
    # signatureの取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    body = request.body.decode('utf-8')
    try:
        # 署名の検証を行い、成功した場合にhandleされたメソッドを呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        return HttpResponseForbidden()
    return HttpResponse('OK')



# フォローイベントの場合の処理
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )


# メッセージイベントの場合の処理
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )

@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    # 画像と音源の場合は保存する
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

参考

Django備忘録: 複数チェックボックス

複数のチェックボックスから値を受け取る方法

複数のチェックボックスやテキストから値を得る方法がわからなかったので調べてみました。 request.POSTからkey-valueで取得しようとすると一つのinputタグしか取得できないため、POSTのメソッドを使用しなければなりません。

HTML

サンプルのHTMLは以下になります。nameが同一で、valueが異なるときを想定しています。

<input type="checkbox" name="test" value="value1" />
<input type="checkbox" name="test" value="value2" />

viewでの受け取り方

今回は関数ベースのviewが前提です。

>>> values = request.POST.getlist('test')  # testはcheckboxのinputタグのname
>>> print(values)
['value1', 'value2']

getlistメソッドについて

request.POSTおよびrequest.GETオブジェクトはdjango.http.QueryDictクラスのインスタンスでした。 そのQueryDictは 辞書型のサブクラスであり、通常の辞書のメソッドに加えてgetlist, lists, urlencodeなどのメソッドも追加されていることがわかりました。 このQueryDictのメソッドを用いてリストを得ることができます。

参考にした公式ドキュメントを貼付します。 https://docs.djangoproject.com/ja/2.0/ref/request-response/#querydict-objects

独学プログラマーを読んでみた

この本を買ったきっかけ

きっかけは第一章を本屋で読んだことでした。私は現在未経験からプログラマーを目指している一員です。プログラミングの勉強を初めて一年くらいがたち、本格的にプログラマーへの転職を考えていました。

そんな中で目にした本書のイントロダクションは印象的でした。特に著者がモチベーションを維持するためのテクニックは衝撃的で、全く思いつかない上に真似できません。未経験からプロになるために必要なことがわかると思い、購入しました。

概要

本書は六部構成になっており、プログラミングの基本から、各ツールの使い方など広く浅い内容であると思います。

本書の対象者

本書はプログラミング経験のない方をメインターゲットとしています。そのため、普段からガリガリ書いているプログラマーの方には少し物足りないかもしれないです。

良かったところ

各章にチャレンジ問題がある

読んだあとにどのくらい身についてるかをチェックできるので助かります。

日本語版の注釈

実際にコードを書くときに日本語を扱う上でのギャップはなく、原著のように読むことができました。 また、各詳細サイトも日本語版のリンクが添付してあり、参考になりました。

データ構造・アルゴリズム

プログラミングの入門本ではあまり触れられない内容に見えますが、本書では紹介されています。 この本で学ぶというようりも、課題の一つとして認識するためという印象です。

Part5

いざ言語の文法や使い方を覚えた状態から"仕事"にするまでに何が必要か書いてあります。 紹介されているのは欧米流ですが、参考になることはとても多いです。

総評

もっと早く手に取れたら良かったです。Pythonの入門本というよりもプログラマーの入門本だと思っています。 これからプログラマーへの道を考えている人にはおすすめの一冊です。

独学プログラマー Python言語の基本から仕事のやり方まで

独学プログラマー Python言語の基本から仕事のやり方まで