- 投稿日:2020-03-23T19:11:33+09:00
Railsにて検索機能を実装 (全体一致や部分一致などのselectボックスを入れ、複数モデルからの検索を可能にする)
備忘録として、なるべく自分の理解している範囲で、考え方の順序なども記載していく。
railsを触って、数週間なので、初心者の人のために
慣れている人や頭のいい人だと、そんなのわかってるよと言われることまで書いていきます。
間違っていたら教えてください。
devise でログイン機能は追加しており
UserモデルとBookモデルを作っています。
検索フォームをログインしている場合、全てのページに表示させたいので
app/views/layouts/application.html.erb のviewに記載していく。
<% if user_signed_in? %>
<%= form_tag(search_path, method: :get) do %>
<%= select_tag 'range' ,options_for_select([['---選択してください---', ''], ['User', '1'], ['Book', '2']]) %>
<%= select_tag 'search', options_for_select([["前方一致","forward_match"], ["後方一致","backward_match"], ["完全一致","perfect_match"], ["部分一致","partial_match"]]) %>
<%= text_field_tag (:word) %>
<%= submit_tag "検索" %><% end %>
<% end %>
全体を<%= form_tag(search_path, method: :get) do %>と<% end %>でくくって
searchアクションにparamsである'range', 'search',(:word)を飛ばす
<%= select_tag 'range' ,options_for_select([['---選択してください---', ''], ['User', '1'], ['Book', '2']]) %>
これの意味は['User', '1']この[]の中のUserを選んだ場合(ドロップダウンリスト)
’1’を'range'というparamsに入れて、コントローラに飛ばすために書いた。
<%= select_tag 'search', options_for_select([["前方一致","forward_match"], ["後方一致","backward_match"], ["完全一致","perfect_match"], ["部分一致","partial_match"]]) %>
これも同様にドロップダウンリストで選べるようにしているのですが
"前方一致"を選んだら"forward_match"を'search'というparamsに入れてsearchコントローラに飛ばすために書いた。
この2つのコードはselectボックスを作るために
select_tag 'paramsを決めてここに書く', options_for_select
params名はなんでも良い。
<%= text_field_tag (:word) %>
これは検索フォームに入力したワードをwordというparamsに入れて上と同様にsearchコントローラに飛ばすために書いた。
つまり3つのparamsをsearchコントローラに飛ばすコードを書いた。
こんな感じのができる。
続いてsearchesコントローラを書いていく
class SearchesController < ApplicationController
def search
@range = params[:range]
search = params[:search]
word = params[:word]if @range == '1'
@user = User.search(search,word)
else
@book = Book.search(search,word)
endend
viewページから飛ばした3つのparamsをそれぞれインスタンス変数かローカル変数の中に入れるために
@range = params[:range]
search = params[:search]
word = params[:word]を書いた。
searchとwordをローカル変数にしたのはmodelのメソッド(今回だとUserモデルとBookモデル)で利用するため、インスタンス変数じゃなくていいから。
if @range == '1'
@user = User.search(search,word)
else
@book = Book.search(search,word)1をユーザーが選んだ場合、@userをviewページで反映させ、2を選んだら@bookを反映させる条件分岐。
ここで.search(search,word)という searchメソッドを定義しなければいけない。
これはモデルで定義していく。
モデルについて
以下がモデルに検索のメソッドを定義する部分です。
ドロップダウンリストでUserを選んだ場合、
Userモデルにsearchメソッドを書いて、それをsearchコントローラーで呼べるようにする。
app/models/user.rb
def self.search(search,word)
if search == "forward_match"
@user = User.where("name LIKE?","#{word}%")
elsif search == "backward_match"
@user = User.where("name LIKE?","%#{word}")
elsif search == "perfect_match"
@user = User.where("#{word}")
elsif search == "partial_match"
@user = User.where("name LIKE?","%#{word}%")
else
@user = User.all
end
endこの書き方はググって出てきたものを真似した。
注意点は自分で定義した(search,word)2つのparamsを書くこと
searchにはviewページに書いた"forward_match"や"backward_match"が入ってきている。
wordにはユーザーが検索フォームに入れたワードが入っている。
User.where("name LIKE?","#{word}%")
このコードはUserモデルから検索ワードにヒットしているかを確認するコードで
nameはUserテーブルのカラム名を記載する。
名前での検索だと思うのでnameにしてある
続いて、ユーザーBookを選んだ場合のメソッドをBookモデルに書いていく
def self.search(search, word) if search == "forward_match" @book = Book.where("title LIKE?","#{word}%") elsif search == "backward_match" @book = Book.where("title LIKE?","%#{word}") elsif search == "perfect_match" @book = Book.where("#{word}") elsif search == "partial_match" @book = Book.where("title LIKE?","%#{word}%") else @book = Book.all endend
Userモデルと同様の書き方にする
そして検索結果を表示するviewページを作成
今回はsearchesフォルダにsearch.index.html.erbを作成したので
routes.rbに
get 'search' => 'searches#search'
追記した。
続いて
search.index.html.erbには
<h2>Results index</h2> <!--books一覧 --> <table class="table table-hover table-inverse"> <thead> <tr> <th></th> <th>Title</th> <th>Opinion</th> <th colspan="3"></th> </tr> </thead> <tbody> <% if @range == '2' %> <% @book.each do |book| %> <tr> <td> <%= link_to(book.user) do %> <%= attachment_image_tag(book.user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg") %> <% end %> </td> <td><%= link_to book.title, book, class: "book_#{book.id}" %></td> <td><%= book.body %></td> </tr> <% end %> </tbody> <% else %> <thead> <tr> <th>image</th> <th>name</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @user.each do |user| %> <tr> <td><%= attachment_image_tag(user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg") %></td> <td><%= user.name%></td> <td><%= link_to "Show", user, class: "user_#{user.id}" %></td> </tr> <% end %> </tbody> <% end %> </table>終わりに
以上が検索機能の実装方法になります。
疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!
- 投稿日:2020-03-23T14:52:08+09:00
table系タグは2種類にグループ分けすると覚えやすい
table系タグは2種類にグループ分けすると覚えやすい
(1)エリア系タグ...表自体のエリアごとの意味を表す
<table>
...「このエリアは表」<thead>
...「このエリアは表の見出し」<tfoot>
...「このエリアは表のフッター」<tbody>
...「このエリアは表のメイン」<tr>
...「ここからここまでが1行」(<th>
や<td>
を囲います)(2)コンテンツ系タグ...表の中身を表す
<th>
...「これが見出しの文字」<td>
...「これが見出し以外(データ等)の文字」example<table> <!-- 開始:表エリア --> <thead> <!-- 開始:見出しエリア --> <tr> <th>見出しA</th> <!-- コンテンツ系タグ:見出し --> <th>見出しB</th> </tr> </thead> <!-- 終了:見出しエリア --> <tbody> <!-- 開始:メインエリア --> <tr> <td>AA</td> <!-- コンテンツ系タグ:見出し以外(データ等) --> <td>BB</td> </tr> <tr> <!-- 開始:1行 --> <td>AAA</td> <td>BBB</td> </tr> <!-- 終了:1行 --> <tr> <td>AaAa</td> <td>BbBb</td> </tr> </tbody> <!-- 終了:メインエリア --> </table> <!-- 終了:表エリア -->この記事を作った経緯:
テーブルを作るとき、よく基本構造やそれぞれのタグの意味を忘れてしまうので自分なりの覚え方を考えました。その備忘録がこの記事です。
- 投稿日:2020-03-23T13:55:22+09:00
HTML,CSSでのwebサイト制作の手順とポイント
はじめに
本記事は
「progateやドットインストールなどでhtml、cssの文法的なインプットはしたが、いざwebサイトを作成する場合にどの手順を踏めば良いのか??」
という人を初学者を対象にhtml、cssを使ってwebサイトを作る際の手順と、その際に念頭におくべき事をまとめたものです。
拙い内容だとは思いますが、参考になれば幸いです!
※本記事はデザインが既にできている場合の手順になります。
①~③の過程は実際にデザインツールか紙を使って書く事を強くお勧めします!!
① デザインの全ての要素を隙間のない□で埋める!
ポイント
・全てを□で隙間なく埋め切る事が重要!
例 赤線にて区切り
↓
② ①で区切った□をさらに1つ1つを細かいパーツに分ける
②-1 まずは大きなパーツから分けていく(青線)
②-2 大きなパーツの中を更に細分化できる所まで分けていく(緑線)
ポイント
・大きな□の中に小さな□が敷き詰められている状態になる
・この□の1つ1つがdiv要素であると考える!!(正確にはdiv以外のタグも使うが、、)③ □の1つ1つに名前(class名)を付ける!!
③-1 赤線で囲った□に名前をつける
③-2 青線で区切った□に名前をつける
③-3 緑で区切った□に名前をつける
ポイント
・大きい部分から名前をつけていく(赤線→青線→緑線)
・名前は基本的に重複なく付ける
・全く同じパーツを複数回使うときは、同じ名前をつける
・名前の付け方(命名規則)はプロジェクト単位で行う→1人でのプロジェクトでないなら、名前を付けた段階でレビューを入れると良い!!参考用:代表的な命名規則について
https://webdesign-trends.net/entry/9214
④ htmlで□をネスト構造(入れ子構造)で書いていく
例:上記のbody1部分
<div class="body1">//赤線 <div class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </div>青線// <div class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </div>青線// <div class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </div>青線// </div>//赤線セマンティクスwebについて
セマンティクスwebはモダンなhtmlの書き方として、html5から導入された「
より要素の内容に沿ったhtmlタグ使おうとする考え方
」です!!
構造の綺麗なhtmlを書く事ができ、SEOなどの対策になります!!先ほどのhtmlは分かりやすさ重視の為に、全ての要素をdivタグで囲いましたが、セマンティクスwebを導入するとdivの代わりに様々なhtmlタグを使用していく事になります。
例
<main class="body1">//赤線 <article class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </article>青線// <article class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </article>青線// <article class="contents">//青線 //緑線 <div class="img"><img src="" alt=""></div>緑線// //緑線 <div class="text"><p>説明文説明文</p></div>緑線// </article>青線// </main>//赤線htmlを書く際はセマンティクスwebを意識してhtmlタグを工夫すると良いでしょう!!
参考用:セマンティクスwebについての詳細
https://developer.mozilla.org/ja/docs/Glossary/Semantics
ポイント
・「赤線の要素→青線の要素→緑線の要素」が入れ子になるようにhtmlを書く
・htmlをまず書き出して、必要としている要素が全て出力されているかブラウザで確かめる⑤ cssにてデザインを整える
htmlでブラウザ上に各要素は出力されているので、cssでデザインに沿ってよしなにスタイリングして下さい。
まとめ
この手順は、「
とにかくブラウザ上で成果物が目で見える状態にする。その上で細かいスタイリングを行なっていく!
」という考え方に基いたものです。こうする事で第三者に対して、早い段階から成果物の進捗を共有しやすくなったり、cssなどの挙動をブラウザでいちいち確認できるといった恩恵が受けられます。
- 投稿日:2020-03-23T13:36:05+09:00
Cordovaのチュートリアルをやってみる
公式チュートリアル:http://ccoenraets.github.io/cordova-tutorial/index.html
Cordovaプロシェクトの作成
Node.jsの最新バージョンをインストール
Cordova CLIのインストール
[windows] npm install -g cordova すでにインストール済みの場合は最新バージョンにアップデート npm update -g cordova [mac] sudo npm install -g cordova すでにインストール済みの場合は最新バージョンにアップデート sudo npm update -g cordovaプロジェクトを保存するディレクトリに移動し、workshopという名前のディレクトリにWorkshopというCordovaプロジェクトを作成。
cordova create workshop com.yourname.workshop Workshopプロジェクトディレクトリに移動。
cd workshopSDKのインストール
workshopディレクトリにて
[iOS]cordova platforms add ios[Android]
https://developer.android.com/studio からインストール後cordova platforms add androidworkshopディレクトリにて、基本的なプラグインを追加
cordova plugin add org.apache.cordova.device cordova plugin add org.apache.cordova.console 上記は旧式らしくエラー出る。 cordova plugin add cordova-plugin-splashscreen cordova plugin add https://github.com/apache/cordova-plugin-console.git#1.0.0 上記で対応可? 詳細は未調査なので後ほど探ります。エラー対応の参考:https://qiita.com/konyavic/items/ca6bec224ddd6abeb0ac
ディレクトリ構成
wwwが、コーディングファイル保存場所
platformsは、iOS/Androidなど異なるプラットフォームのためのアプリケーションをビルドする場所。
いじらない。プラグインはpluginsにインストールされる。
アプリケーションパラメータ(名前、作成者など)はconfig.xmlに保存される。
- 投稿日:2020-03-23T08:45:46+09:00
UIkit3の拡張用CSSファイル(UIkit Plus α)を公開してみた
最近Web開発をする際のフロント側のCSSフレームワークに、
UIkit3
をよく使用しています。(特に指定が無い限り)
個人的にBootstrap
やMaterialize
などいろいろと使ってきましたが、自分的にこれが一番使いやすかったので。(あとデザインが好き)ただし
UIkit
のみで全てを賄える訳ではもちろんないので、必要に応じて独自のCSS定義を追加するのですが、そういった時には決まって、
- そういえば以前にも同じ定義を書いたなようなと思いつつ同じ定義をまた書く
- 以前作ったものを探してきてコピーして使用する
ということを繰り返してました。
そんな経緯から、なら 良く使うものをまとめてしまえ! と思い至り、
せっかくまとめたのなら、どれだけ需要があるのか分かりませんが、 公開してしまえ! とw
そしてUIkit
がもっと普及するといいな。というわけで公開したのが UIkit Plus α になります。
UIkit3
の使用が前提となりますが、ご自由にお使いいただければと。余談ですが、
昔は自分にもデザインはデザイナーさんの仕事だよね?と思っていた時もありました。
ただ最近はこういったCSSフレームワークの普及などにより、ある程度なら簡単にデザインが組めるようになり、開発時の線引きも曖昧に。
さすがにある程度はデザインをいじれないとマズイかなと思い、手を出したところ、、、見事にハマってしまいました。(笑)
開発するうえで、機能的な要件を満たすことは当然ですが、見栄えというのも大事ですよね。UIkit Plus α
UIkit3と合わせて取り込むことで、少し拡張(使用できるCOMPONENTSを追加)することができます。
日頃の開発の中で今後も使いそうだなと思ったものをCOMPONENT化しています。(独断と偏見によりw)
同じくUIkitを使用する方の手助けになれば幸いです。
まだまだCOMPONENTの数は少ないですが、今後も随時COMPONENTを追加していく予定です。使い方や追加されるCOMPONENTSの詳細は下記ページをご覧ください。
実際のデザインやサンプルコードを確認頂けます。
Document「こんなCSS定義をよく使うんですけど」などを
GitHubのIssueに書き込んでくれれば、もしかしたら(もしかしたらですよ?)追加されるかもしれません。UIkit Plus α で追加されるCOMPONENTS
- Paragraph - 段落表示
- Sub Text - サブテキスト
- Box List - ボックスタイプのリスト
- Border Table - 枠線タイプのテーブル
- Titled Preformatted Text - タイトル付き整形済みテキスト
- Side Fix Nav - 右上固定のナビゲーション
- Floating Button - フローティングボタン
- Margin Mini - 極小のマージン設定
- Message Box - メッセージ表示
- 投稿日:2020-03-23T08:23:15+09:00
HTML:フォームを作ろう
フォームについて
今回はフォームを作ってきます
フォームとは、ユーザーがテキスト入力したり、選択したりできるものです。
お問い合わせや、検索ボックス、会員登録などの際にWebページでよく見かけるのではないでしょうか。このフォームは実は多くのパーツを組み合わせて作られているのです。
そのパーツを1つ1つ勉強していきましょう今回の記事
- フォーム欄を作るformタグ
- テキスト入力欄
- ラジオボタンを作る
- チェックボックスを作る
- 送信ボタンの作り方
- セレクトボックス
- 複数行テキスト入力欄
- より使いやすいフォームにするには
フォーム欄を作るformタグ
<form>タグとは、フォームを作成するためのタグで、フォームを作る際には<form>タグで使用するすべてのパーツを囲う必要があります。
記述する主な属性は以下の通りです。
属性 用途 action データ送信先ページを指定 method データの送信方法を指定。主にgetかpostを入力 name フォームの名前を指定 フォームタグの例<form action="./Homeservlet" method="post" name="contact_form"> <!-- ここにフォームの部品が入ります。 --> </form>この例だと、入力した内容がHomeservlet.javaに送られます。(JSP,Servlet使用の場合)
次からフォームの部品について見ていきます。
多くのパーツはinputタグで書いていきます。テキスト入力欄
部品部分はinputタグのtype属性によって変わってきます。1行のテキスト入力の場合は、type属性の中にtextと書きます。
1行テキストの例お名前:<input type="text">入力欄に最初からテキストを表示
入力欄に最初からテキストを表示することも可能です。
placeholder属性を使用します。テキスト初めから表示お名前:<input type="text" placeholder="名字 名前">色々な種類の1行テキスト
1行テキストには入力する内容が多々あります。
type属性の中に様々なものを指定できます。この属性値をサポートしているブラウザで正しい書式をチェックしてくれます。
属性値 用途 text 1行テキスト(初期値) search 検索するときのテキスト メールアドレス tel 電話番号 url WebサイトのURL ラジオボタンを作る
複数ある選択肢のうち、1つのみ選択してももらいたい時にはラジオボタンを使用します。
ユーザーが一つをクリックして選択すると、その他の選択肢は自動的に選択できなくなります。
ラジオボタンを作る場合は、type属性の中にradioと書きます。主な属性
属性値 用途 name ラジオボタンの名前 value 送信される選択肢の名前 checked 最初から選択されている状態にするとき指定 ラジオボタンの例性別: <input type="radio" name="gender" value="男">男 <input type="radio" name="gender" value="女" checked>女 <input type="radio" name="gender" value="その他">その他複数の選択肢があるラジオボタンでは、それぞれに同じname属性の値を付けることで1つのグループとしてまとめられます。checked属性を指定したラジオボタンは最初から選択状態になります。
チェックボックスを作る
チェックボックスはラジオボタンのように複数ある選択肢から選んでもらうパーツですが、ラジオボタンと違いチェックボックスの場合は複数の項目を選択可能です。
チェックボックスを作る場合は、type属性の中にcheckboxと書きます。主な属性
属性値 用途 name チェックボックスの名前 value 送信される選択肢の値 checked 最初から選択されている状態にするとき指定 チェックボタンの例好きな色: <input type="checkbox" name="color" value="赤" checked>赤 <input type="checkbox" name="color" value="青">青 <input type="checkbox" name="color" value="黄">黄 <input type="checkbox" name="color" value="緑">緑ラジオボタンと同じく、それぞれに同じname属性の値を付けることで1つのグループとしてまとめることができます。checked属性も同様です。
チェックボックスは、趣味や利用目的など、複数の答えが考えられるときに利用しましょう。送信ボタンの作り方
フォームに入力した内容を送信するパーツです。
送信するパーツを作る場合は、type属性の中にsubmitと書きます。
ボタン上に表示させるテキストは「送信」でなくても大丈夫です。例えば検索フォームなら、「検索」、会員登録フォームなら「登録」と、用途に合わせて変えるといいでしょう。主な属性
属性値 用途 name ボタンの名前 value ボタンに表示されるテキスト 送信ボタンの例<input type="submit" value="送信">セレクトボックス
セレクトボックスをクリックすると、選択肢が表示されるパーツです。選択肢全体を<select>タグで囲み、選択項目はそれぞれ<option>タグで囲みます。
<select>タグの主な属性
属性値 用途 name セレクトボックスの名前 multiple ShiftまたはCtrlキーで複数の項目を選択可能にする <option>タグの主な属性
属性値 用途 value 送信される選択肢の値 selected 最初から選択されている状態にするときに指定 セレクトボックスの例<select name="bloodtype"> <option value="A">A</option> <option value="B">B</option> <option value="O">O</option> <option value="AB">AB</option> <option value="不明" selected>不明</option> </select>複数行テキスト入力欄
<textarea> タグを使えば、複数行にわたるテキストを入力できます。お問い合わせ内容やメッセ時を入力するときに使われます。<textarea> タグで囲まれた部分が初期値として表示されます。
<textarea> タグの属性
属性値 用途 name 入力フィールドのの名前 cols 一行当たりの最大文字数の目安を指定する rows 表示行数を指定する placeholder ユーザー入力に関するヒントを短めのテキストで指定 複数行テキスト入力の例<textarea name="message" cols="30" rows="10">メッセージを入力</textarea>上記のように<textarea> タグで囲まれた部分は入力欄をクリックしても消えないので、 placeholder属性で指定するとよいでしょう。
複数行テキスト入力の例<textarea name="message" placeholder="メッセージを入力" cols="30" rows="10"></textarea>より使いやすいフォームにするには
フォームを設置する時、例えば1行テキスト入力欄横に「名前」「電話番号」などの文字表示させます。これらのテキストのことを「ラベル」と呼びます。ラベルをつけることでより使いやすいフォームになります。
ラベルには<label>タグを使います。ラジオボタンやチェックボックスをクリックする際などに使用します。
属性値 用途 for フォーム部品とラベルを関連付ける 関連付けたいフォームにはid属性を付けます。このfor属性とid属性の値を同じものにしておけば関連付けられます。
ラベルの例<input type="checkbox" name="travel" value="日本国内" id="japan"> <label for="japan">日本国内</label> <input type="checkbox" name="travel" value="ヨーロッパ" id="europe"> <label for="europe">ヨーロッパ</label> <input type="checkbox" name="travel" value="東南アジア" id="asia"> <label for="asia">東南アジア</label>これで文字の部分をクリックしてもチェックがつくので、使いやすいですね
- 投稿日:2020-03-23T05:02:29+09:00
初心者によるプログラミング学習ログ 270日目
100日チャレンジの270日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
270日目は、おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) March 22, 2020
270日目
・webサイト模写
・noteの、コーディング教材1つ終了#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-03-23T01:59:26+09:00
モバイル画面でbackground-attachement: fixed; が効かない
モバイル画面でbackground-attachement: fixed; が効かない場合
ちょっとおしゃれなホームページを作ろうと思ったら、
簡単にできるものとして背景固定のパララックス風のページデザインがありますよね。その際は
hogehoge.cssbody{ background-image: url('hogehoge.jpg'); background-attachment: fixed; background-size: cover; }とかで背景画像を固定するのが一般的かな?
が、実はこの
background-attachment: fixed;
iOSではうまく動かないんです。正しくは、iOSモバイル画面で見てみると背景画面が固定されません、、、
background-attachment:fixed;とbackground-sizeはモバイルだと衝突するみたいです。じゃあどうするの?ってことで
以下解決策bodyに::before疑似要素で背景画像を指定!!
hogehoge.cssbody{ width:100%; z-index: 0; } body::before{ content: ""; display: block; position: fixed; top: 0; left: 0; z-index: -1; width: 100%; height: 100vh; background-image: url('hogehoge.jpg'); background-size: cover; }bodyの擬似要素(z-index:-1;)をbody(z-index:0;)の一枚裏におけばおk
content: "";
→ 私のような初学者は『これ何?』とお思いだろうが、
::after とか ::before についてはcontentプロパティがないと要素として全く見えなくなります。
のでつけときましょう。これで::beforeを使ったbackground-attachment: fixed;の画面を実装することができました。
モバイルでの背景固定パララックス風デザインを実装する際は是非ご参考に!
- 投稿日:2020-03-23T01:06:30+09:00
Stripe決済のDjangoでの実装
初めに
Stripeを使えば比較的簡単に決済機能を実装できます。この記事ではStripeが用意した決済フォームにページをリダイレクトさせることで、自サイトではクレジットカード情報を保持することなく支払い機能を自サイトに組み込むことができます。
基本はStripeのドキュメントとGithub上のサンプルを基にして実装しています。また、Djangoで実装します。
正直、試行錯誤しながら適当に作ったので実装に関して突っ込みどころ満載です。最終的にはStripeのサンドボックスの劣化版が実装されます。また、以下の情報は2020年3月時点のものです。
環境設定
- Python==3.8.1
- Django==3.0.4
- stripe==2.43.0
- django-environ==0.4.5
Step0: 前準備
適当にディレクトリを作成する。
mkdir stripe-test cd stripe-test
pipenv
を使って仮想環境を構築します。pipenv --python 3.8 pipenv install django stripe django-environ
pipenv shell
で仮想環境を起動する。pipenvについてさらに詳しく知りたい場合はPipenvを使ったPython開発まとめを参照。# shell起動 pipenv shell
Django
プロジェクトを作成する。django-admin startproject stripe_test . django-admin startapp checkout
Stripeにアカウント登録
Stripeにアカウント登録する。アカウント作成やダッシュボードの見方についてはRailsのdeviseとStripe(とBootstrap)の組み合わせで、ただただユーザー登録させたユーザーに定期更新購読させるだけのappを作りました②に詳しい。
公開ビジネス情報
を入力する。2020年3月現在だと以下のURL。次に開発用の公開キーとシークレットキーを確認する。2020年3月現在だと以下のURL。
Django Setting
Djangoのsettingを以下のようにする。
.env
ファイルを取り込むのにdjango-environ
を使用している。stripe_test/stripe_test/settings.pyimport os import environ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PROJECT_NAME = os.path.basename(BASE_DIR) # django-environから環境変数を読み込む env = environ.Env() env.read_env(os.path.join(BASE_DIR, '.env')) ### 省略 ### INSTALLED_APPS = [ ..., 'stripe',#追加 'checkout.apps.CheckoutConfig', # 自作 チェックアウト ] ### 省略 ### TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #デフォルトから変更 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ### 省略 ### # Static files (CSS, JavaScript, Images) STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY') STRIPE_PUBLISHABLE_KEY = env('STRIPE_PUBLISHABLE_KEY')
.env
には例えばこういう設定を行う。使用通貨がアメリカドル(usd)の場合はセント単位で計算するので1234と入力した場合は$12.34となる。.env# Stripe API keys STRIPE_PUBLISHABLE_KEY=pk_12345 STRIPE_SECRET_KEY=sk_12345 # Checkout settings DOMAIN=http://localhost:8000/ BASE_PRICE=1099 CURRENCY=usdStep1:サーバー側でチェックアウト・セッションを実装する
セッションの作成はサーバー側で行う。Python + Django でJSONの送受信を参考にして、以下のようにDjangoを使って、Checkout Sessionを作成する。
stripe_test/checkout/views.py# -*- coding: utf-8 -*- import logging import os import json from django.shortcuts import render from django.conf import settings from django.views import generic import stripe from django.http.response import JsonResponse from django.views.decorators.csrf import csrf_exempt logger = logging.getLogger(__name__) @csrf_exempt def onetime_payment_checkout(request): if request.method == 'POST': data = json.loads(request.body) domain_url = os.getenv('DOMAIN') stripe.api_key = settings.STRIPE_SECRET_KEY try: # Create new Checkout Session for the order # ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param checkout_session = stripe.checkout.Session.create( success_url=domain_url + "checkout/success?session_id={CHECKOUT_SESSION_ID}", cancel_url=domain_url + "checkout/canceled/", payment_method_types=["card"], line_items=[ { "name": "Pasha photo", "images": ["https://picsum.photos/300/300?random=4"], "quantity": data['quantity'], "currency": os.getenv('CURRENCY'), "amount": os.getenv('BASE_PRICE'), } ] ) logger.debug( str(checkout_session)) return JsonResponse({'sessionId': checkout_session['id']}) except Exception as e: logger.warning( str(e) ) return JsonResponse({'error':str(e)})Step2:チェックアウトにリダイレクト
基本的にJavascriptを使って実装していく。実現していることは
stripe.js
を使ってStripe側にデータを渡して、Stripeで決済してもらった後に自サイトに戻る。stripe_test/templates/base.html{% load i18n static %} <!DOCTYPE html>{% get_current_language as LANGUAGE_CODE %} <html lang="{{ LANGUAGE_CODE|default:"en-us" }}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <title>{% block title %}{% endblock %}</title> <script src="{% static 'js/jquery-3.3.1.slim.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> {% block additional_script %} {% endblock %} </head> <body> <div class="container"> {% block content %} {% endblock %} </div> </body> </html>stripe_test/templates/checkout/checkout_test.html{% extends "base.html" %} {% load i18n static %} {% block title %}Checkout_test{% endblock title %} {% block additional_script %} <script src="https://js.stripe.com/v3/"></script> <script src="{% static 'checkout/js/script.js' %}"></script> {% endblock additional_script%} {% block content %} <div> <h1 data-i18n="headline"></h1> <h4 data-i18n="subline"></h4> <div class="pasha-image"> <img src="https://picsum.photos/280/320?random=4" width="140" height="160" /> </div> </div> <div class="quantity-setter"> <input type="number" id="quantity-input" min="1" max="2" value="1" /> </div> <p class="sr-legal-text" data-i18n="sr-legal-text"></p> {% csrf_token %} <button id="submit" >PAYMENT</button> </section> <div id="error-message"></div> </div> {% endblock content %}自作Scriptは以下のサイトを参考にした
- 参考Github
- Fetch を使うstripe_test/static/checkout/js/script.js/* Handle any errors returns from Checkout */ var handleResult = function(result) { if (result.error) { var displayError = document.getElementById("error-message"); displayError.textContent = result.error.message; } }; // Create a Checkout Session with the selected quantity var createCheckoutSession = function() { var inputEl = document.getElementById("quantity-input"); var quantity = parseInt(inputEl.value); return fetch("/checkout/create-checkout-session/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ quantity: quantity, }) }).then(function(result) { return result.json(); }); }; /* Get your Stripe publishable key to initialize Stripe.js */ fetch("/checkout/config") .then(function(result) { return result.json(); }) .then(function(json) { window.config = json; var stripe = Stripe(config.publicKey); // Setup event handler to create a Checkout Session on submit document.querySelector("#submit").addEventListener("click", function(evt) { createCheckoutSession().then(function(data) { stripe .redirectToCheckout({ sessionId: data.sessionId }) .then(handleResult); }); }); });HTMLとJavascriptが実装できたら、checkout_test.htmlが見られるように
views.py
を以下を追加する。stripe_test/checkout/views.pyclass IndexView(generic.TemplateView): template_name = "checkout/checkout_test.html" def get(self, request, *args, **kwargs): return render(request, self.template_name )とりあえずは
checkout
アプリのマッピング情報を記述して、最初のページだけ見られるようにする。これはcheckout/urls.py
を以下のように編集することで実現できる。stripe_test/checkout/urls.pyfrom . import views from django.urls import path,include app_name = 'checkout' urlpatterns = [ path('',views.IndexView.as_view(), name="index"), ]Djangoアプリではない、ホームページ側の
urls.py
(この記事ではstripe_test/stripe_test/urls.py
)を以下のように修正する。stripe_test/stripe_test/urls.pyfrom django.contrib import admin from django.urls import path,include from django.views.generic import RedirectView urlpatterns = [ path('admin/', admin.site.urls), path('', RedirectView.as_view(url='/checkout')), path('checkout/', include('checkout.urls')), ]上記自作のjavascript(
stripe_test/static/checkout/js/script.js
)ではfetch("/checkout/config")
を行っているが、これはjsonでstripeAPIの公開キーを受け取っている。サーバー側の実装は以下のとおり。stripe_test/checkout/views.py@csrf_exempt def stripe_config(request): if request.method == 'GET': stripe_config = { 'publicKey': settings.STRIPE_PUBLISHABLE_KEY, 'basePrice': os.getenv('BASE_PRICE'), 'currency': os.getenv('CURRENCY'), } return JsonResponse(stripe_config, safe=False)stripe_test/checkout/urls.pyfrom . import views from django.urls import path,include urlpatterns = [ ..., path('config/',views.stripe_config), ..., ]
python manage.py runserver
でとりあえずサイトを起動してhttp://localhost:8000/
にアクセスすると以下の感じの画面が出るはずだ。ただ、これだと
PAYMENT
を押しても、何も起きないはず。なぜなら、ボタン押されたあとの実装を行っていないから。具体的には自作javascriptのcreateCheckoutSession
関数はとあるURLにPOSTしているが、Django側で設定していないので、設定を行う必要がある。それにはurls.py
にcreate-checkout-session
を追加すればいい。stripe_test/checkout/urls.pyurlpatterns = [ ..., path('create-checkout-session/',views.onetime_payment_checkout , name='create-checkout'), ..., ]以上まで実装を行えば、Stripe側にURLが飛んで決済情報を入力する画面が表示されるはずです(参考:Stripe Checkout)。後は、決済情報を入力し終わったリダイレクト先のページ(
success.html
とcanceled.html
)を実装していけば、一通り決済はできるようになっている。テスト
テスト用の決済カードは複数用意されているので、適宜選ぶ。特にこだわりがないのならば
4242424242424242
を使用。もし、うまく実装できていればダッシュボード上に支払いが反映されているので、確認してみてください。
参考:Github
上記の実装はGithubに公開しているので、参考にしてください。
- Github上のサンプル参考サイト