20211026のMySQLに関する記事は5件です。

Lambda (SAM CLI) + MySQL(docker-compose)を利用した REST API作成方法メモ

ローカル環境でLambda(SAM) + MySQL(docker-compose)を利用したAPIを作成する方法についてメモする。 AWS SAM(Serverless Application Model) サーバーレスアプリのデプロイに特化したCloudFormationの拡張機能。 YAMLもしくはJSONでリソース定義する。 SAM CLI SAMを利用した開発サポートツール サーバーレスアプリのひな形作成、ビルド、AWSへのデプロイができる。 開発者のローカル環境でLambdaをDockerコンテナ起動できる。 構成 準備 SAM CLIインストール brew tap aws/tap brew install aws-sam-cli 実装 MySQL docker-compose.yml MySQL起動用 version: "3.3" networks: container-link: name: docker.internal services: db: image: mysql:5.7 container_name: db environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: sample_db MYSQL_USER: mysqluser MYSQL_PASSWORD: mysqlpass networks: - container-link volumes: - ./db/data:/var/lib/mysql - ./db/my.cnf:/etc/mysql/conf.d/my.cnf - ./db/initdb.d:/docker-entrypoint-initdb.d ports: - 3308:3308 command: --port 3308 tty: true 起動 docker-compose up -d ※起動後テスト用のテーブル作成とデータ登録を行う。 ※seq,name,ageカラムを持つsampleテーブルを作成している。 Lambda ひな形作成 sam init ※ランタイムはPython3.8を選択。 template.yml Hello World Exampleを利用する。 今回はハローワールド用テンプレートをそのまま利用する。 requrements.txt requests mysql-connector-python ※MySQLコネクター追加。 Lambda処理(app.py)修正 import json import mysql.connector def lambda_handler(event, context): # MySQLからデータ取得 # それぞれの環境に合わせて記述 sql = "select * from sample;" conn = mysql.connector.connect( host = "db", port ="3308", user = "mysqluser", password = "mysqlpass", database = "sample_db" ) cur = conn.cursor(dictionary=True) cur.execute(sql) results = cur.fetchall() cur.close() conn.close() # レスポンスボディ生成 response = [] for result in results: response +=[ { "name":result["name"], "age":result["age"] } ] return { "statusCode":200, "body":json.dumps({"results":response}) } 動作確認 ビルド sam build --use-container 実行 sam local start-api --docker-network docker.internal リクエスト GET /hello HTTP/1.1 Host: localhost:3000 Content-Type: application/json レスポンス { "results": [ { "name": "test", "age": 25 }, { "name": "test2", "age": 26 } ] } 参考情報 Architecting and Building - レガシーシステムにサーバーレスを利用して素早くサービスを追加するには【後半】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQLのストアドプロシージャを学ぶ

ストアドプロシージャとは ざっくり言うとDBMS(ここではRDBMSのこと)内で動作するプログラムのこと。 SQL文を連続で実行したり、変数・条件分岐・繰り返し構文を使っていわゆるプログラムちっくな処理をDBで実現することができる仕組み。SQLは単体では手続き型言語のような処理を書くことはできないが、ストアドプロシージャを使うことで、DBMSの中で動作するプログラムにSQLを組み込むことができる。 SQLをずっと触ってきた人でも意外と知らない人が多い。 ストアドプログラムと呼ばれたりもする。(この辺りはDBMSによる) ストアドプロシージャのメリットとデメリット メリット 処理の高速化が期待できる 複数のテーブルに更新をかけたり、大量のデータに対して連続で更新をかけたりする場合、通常のプログラム言語で処理しようとすると何度もDBにアクセスする必要が出てくるので、DBへの接続回数が増えれば増えるほどプログラムの負荷が増してしまう。また、APサーバとDBサーバが物理的に離れている場合、ネットワークの負荷もプラスされるためDBへのアクセス回数が増えるとパフォーマンスに影響を与える可能性が高い。 その点ストアドプロシージャはDBMS内で完結する処理なので、ネットワークの負荷やDB接続時の負荷は呼び出し時の1回だけで済む。そのため、連続して複数のテーブルに更新をかけたり、対象のデータに連続でアクセスする処理を行う場合にはパフォーマンス的に大きなメリットがある。 デメリット DBMSによって仕様が大きく異なる DBMS製品は現在色々な種類がある。Oracle DB, SQL Server, DB2, MySQL, PostgreSQL, SQLite, ... 製品によって特徴は大きく異なるが、SQLに関してはどのDBMSもSQL標準規格にある程度準拠して作られているので、差はそれほど大きくない。しかしながら、ストアドプロシージャに関しては、別のプログラミング言語というレベルで構文が異なったりする。なので案件変わって使用するDBMSが変わると、ストアドプロシージャ使うにしても構文が違うので同じ知識をそのまま流用できなかったり、システムのDBを移行する必要が出たときにストアドプロシージャを使っている場合にはほぼ作り直す必要が出てくる。その点がデメリット。 とはいえ、ストアドプロシージャは一般的なプログラミング言語にあるオブジェクト指向のような複雑な概念はなく、SQL、変数、条件分岐、繰り返し、関数あたりの概念を理解していれば学習コストは比較的低い。またDBMSによって構文が違うとは言ってもカーソルなどのストアドプロシージャ特融の概念はある程度共通しているので、一つのDBMS製品でストアドプロシージャを学習していれば他のDBMSでも応用は効く。 ちなみに、この記事ではMySQLのストアドプロシージャについて書いているが、正直言うとMySQLのストアドプロシージャは他のDBMS製品のそれと比べると比較的機能は少ない。(そういう意味では学習コストは低いかも。) ストアドプロシージャに関しては、有償であるOracle DBやSQL Serverの方が圧倒的に優れているので、ストアドプロシージャをガッツリ活用する開発を行う場合には有償のDBMSを使うことを強くお勧めします。 (じゃあなぜMySQLのストアドプロシージャの記事を書くかと言えば、仕事で使う機会があったので、単にその時の知識の整理とアウトプット目的です。) ストアドの種類 DBMSでプログラムを書く仕組みは、大きく3つある。 ファンクション プロシージャ トリガー 一般にストアドプロシージャといえばこの中のプロシージャのことを指すことが多いが、文脈によっては全部ひっくるめてストアドプロシージャと呼ぶこともあるような気がする。プロシージャと区別するために3つまとめてストアドプログラムと呼ばれたりもする。ここではとりあえず3つひっくるめたものをストアドと呼ぶことにする。 この3つは大体どのDBMSにも備わっている機能だが、特性や制限などはDBMSによって大きく異なるので注意。 それぞれの特徴 以下、ファンクション、プロシージャ、トリガーそれぞれの特徴になりますが、あくまでMySQLに限定した話です。 ファンクション 引数を受け取り、戻り値を返すもの。関数 SELECT文の中で呼び出すことが可能 トランザクション制御はできない ファンクション(関数)はSELECT文の中でも使用するものなのでイメージがしやすいでしょう。 SELECT文で使える関数として文字列を結合するconcat関数や、数値を切り捨てするtruncate関数などがあるが、これがいわゆるファンクション。DBMSで最初から色々と用意されているが、自作することが可能。 ファンクションの中でupdate, delete, insertなどの更新系SQLも実行可能だが、トランザクション制御はできない。 プロシージャ SELECT文から呼び出すことはできない。CALLを使って呼び出す。 引数を受け取ることができるが、戻り値はない。ただし、引数でOUT, INOUTの引数を受け取ることができる。 トランザクション制御が可能 ファンクションと比較すると呼び出し方やトランザクション制御の可否などで違いがある。 どちらを使うかはケースバイケース。 トリガー 特定のテーブルが更新されたときに自動的に呼び出される処理 old, newというキーワードが使用可能 トランザクション制御はできない トリガーはファンクションやプロシージャと違って意図的に呼び出すことができない。 その名の通り、テーブルへの更新(insert, update, delete)がトリガー(引き金)となって処理が呼び出される。 そのため引数を受け取ったり、戻り値を返したりすることはできない。 トリガーを定義する際に、更新対象のテーブル・更新の種類(insert, update, delete)・更新の前か後か(before, after)を指定する。 更新前のレコードの値をold、更新後のレコードの値をnewというキーワードで取得することができる。 トリガーの中でプロシージャを呼び出したりすることも可能。 それぞれの具体例 説明だけではイメージがしにくいと思うので、ここから具体例。 ファンクションの例 MySQLのファンクションは宣言時に「deterministic」か「not deterministic」かを宣言する必要がある。 これは他のDBMSのファンクションでは意識したことがなかったので最初はよくわからなかったけれど。 deterministicとは翻訳すると「決定論的」という意味でいまいち理解しにくいが、要は引数によって戻り値の値が必ず一意に定まるかどうかということらしい。 関数型プログラミング言語でいうところの参照透過性があるかどうかと同義と解釈している。(間違っていたらごめんなさい。。) deterministicなファンクションの例1 delimiter // -- 区切り文字変更 -- 関数の宣言 create function absolute(num double) returns double deterministic begin -- 処理開始 if num > 0 then return num; else return num * -1; end if; end // delimiter ; -- 元に戻す 呼び出し select absolute(100); -- 100 select absolute(-40); -- 40 解説 ファンクションの概要 数値を引数に受け取って絶対値を返却するするファンクション。(もともと標準でabsという関数があるが、サンプルのため別名で同じことができるファンクションを作成) delimiter delimiterは区切り文字のこと。SQLではデフォルトではセミコロン(;)で1文が終了するが、ファンクションなどを作成する際は処理の途中にセミコロンが入るため、一度区切り文字をセミコロン以外に変更する必要がある。ここでは「//」としているが、セミコロン以外でファンクションの中で使用されない文字列であれば何でも良い 宣言 create以降がファンクションの宣言。absoluteがファンクション名。 ()の中のnumが引数、returns intは戻り値の型。この辺りは他のプログラミング言語を知っていれば特に問題ないかと。 この関数は引数によって戻り値の値は一意に決まるので、deterministicをつけている。 処理 処理内容はbegin ~ end の間に定義する。 変数の定義もこの中で行う。ここではif文を使って条件分岐によって戻り値を変えている。 ここではifを使用しているがcaseなども使用可能。 deterministicなファンクションの例2 delimiter // -- drop function total // create function total(start_num int, end_num int) returns int deterministic begin declare sum int default 0; -- 変数宣言 -- ループ処理 label_loop: loop set sum = sum + start_num; set start_num = start_num + 1; if start_num >= end_num then leave label_loop; -- ループ抜ける end if; end loop; return sum; end // delimiter ; 呼び出し select total(10, 100); -- 4905 解説 ファンクションの概要 start_numからend_numまでの値を全て合計した値を返す 変数 引数で受け取った値は変数として使用可能。 引数以外で変数を使用したい場合は、declareで処理を書く前に定義する必要がある。 ここではsumが変数名。intは型で、defaultキーワードでデフォルト値を設定可能。 処理の中で変数に値をセットするには、setキーワードを使用する。 ループ処理 MySQLのループ処理では、ラベルを付けて、ループを抜ける条件を満たしたときにleaveで処理を抜けるようにする。 ここではloop構文を使用しているが、repeat構文でのループも可能。 not deterministicなファンクションの例 続いてnot deterministic、つまり引数によって戻り値が一意に定まらないファンクションの例。 ここでは消費税率を持つテーブルから消費税を取得し、引数で受け取った値の税込金額を算出するファンクションを考える。 taxテーブル id value start_date 1 0.05 1997-04-01 2 0.08 2014-04-01 3 0.1 2019-10-01 delimiter // create function getPrice(price int) returns int not deterministic begin -- 変数宣言 declare tax double default 0.1; -- 消費税の取得 select value into tax from tax where start_date <= current_date order by start_date desc limit 1; return truncate(price + (price * tax), 0); end // delimiter ; 呼び出し select getPrice(100); -- 110 解説 ファンクションの概要 消費税率のテーブルから現在の日付をもとに税率を取得し、消費税込みの金額を計算して返却する。 (実際の業務の場合は軽減税率などの考慮も必要だが、ここでは省略) 実行した日付によって取得される値が異なるので、引数が同じでも毎回同じ結果が変えるわけではないという点で、not deterministicをつけている。 select ... into ... ストアドの処理の中ではselect ... into ... 構文を使用することで、select文の取得結果を変数にセットすることができる。 このとき、select句で指定したカラムの数と、変数の数が一致していないとエラーになるので注意。(他のDBMSだとレコード型などの概念で一括でセットされる仕組みがあるが、MySQLにはないようなので注意) また、この書き方の場合は取得されるレコードが1件でないとエラーになるので注意。 取得されるレコードが複数ある場合は後述するカーソルを利用する必要がある。 プロシージャの例 ここでは売り上げデータを記録したテーブルから、集計用のテーブルにデータを更新する処理を考える。 集計元になるテーブルは以下。 salesテーブル id item_code sales_date price 1 001 2021-10-10 3000 2 002 2-21-10-17 5000 3 001 2-21-10-19 7000 4 002 2-21-10-25 4000 5 003 2-21-11-03 6000 6 002 2-21-10-05 8000 ここから月単位で集計して以下のようにしたい。 monthly_total_price id month total 1 2021-10 19000 2 2021-11 14000 プロシージャの例 delimiter // -- drop procedure calc_toal // create procedure calc_toal(in pmonth varchar(7), inout pcount int) begin declare wprice int default 0; declare sum int default 0; declare done int default false; -- カーソルの宣言 declare curSales cursor for select price from sales where left(sales_date, 7) = pmonth; -- handlerの宣言 declare continue handler for not found set done = true; declare exit handler for sqlexception, sqlwarning begin -- エラー情報を変数に格納 get diagnostics condition 1 @sqlstate = returned_sqlstate, @errno = mysql_errno, @text = message_text; select @sqlstate, @errno, @text; rollback; -- エラーが起きたときrollbackする end; -- トランザクション開始 start transaction; -- 事前削除 delete from monthly_total_price where left(month, 7) = pmonth; -- レコード件数分ループして合計値を求める open curSales; read_loop: loop fetch curSales into wprice; if done then leave read_loop; end if; set sum = sum + wprice; set pcount = pcount + 1; end loop; close curSales; insert into monthly_total_price (month, total) values (pmonth, sum); commit; end // delimiter ; 確認 set @count = 0; -- パラメータ初期化 call calc_toal('2021-10', @count); -- プロシージャの実行 select @count; -- パラメータ確認 select * from monthly_total_price; -- プロシージャの結果の確認 結果 @count 5 id month total 1 2021-10 27000 解説 処理概要 引数で受け取った年月からsalesテーブルのレコードを絞り込み、集計用テーブルに更新を行う。 正直なところこの程度の集計ならgroup by で簡単に解決できるので、プロシージャにするほどの処理ではないけれど、プロシージャやカーソルの概念を説明するためにあえてこんな処理をしていることをご理解下さい。 引数 プロシージャの引数にはin, out, inoutのいずれかを指定することができる。 inは外部から受け取る専用の引数。 outはプロシージャの呼び出し元に値を渡すことができる。 inoutは、inとout両方の性質を持つ(外部から受け取りつつ、再代入した値は外部に渡すことができる)。 プロシージャはファンクションと違って戻り値がない(returnが書けない)ので、呼び出し元に値を渡したい場合にはoutかinoutの引数を利用する。 宣言部 宣言部では変数、カーソル、ハンドラの定義が可能。 定義する順番が変数→カーソル→ハンドラの順番でないとエラーになるようなので注意。 カーソルについては後述。 ハンドラでは、エラーが起きた場合の処理、カーソルでレコードが見つからなかった場合の処理を定義できる。 トランザクション制御 プロシージャではトランザクション制御が可能。 start transactionでトランザクションが開始される。 エラーが起きたときにロールバックしたい場合、sqlexceptionやsqlwarningのハンドラの中でロールバックを行う。 このプロシージャではロールバックと同時に、エラー情報をパラーメータにセットしてselectするようにしている。(MySQLのプロシージャでトランザクション制御するときのテンプレ的な感じらしい。) プロシージャの呼び出し プロシージャを呼び出すには「call プロシージャ名」とする。 out, inoutの引数がある場合には値が上書き可能なパラメータをセットする。 カーソル カーソルはどのDBMSのストアドでも存在する重要な概念なので別で見出しを設けて説明。 ただし細かい使い方はDBMSによって微妙に異なるので注意。 カーソルとは ざっくり言うとselect文の結果を1行ずつ処理するための仕組み。 「declare カーソル名 for select文」によりカーソルを定義することができる openでカーソルの処理開始、処理が終わった場合はcloseする。 fetchで1行ずつデータを取得することができる。このとき、into でselect句で取得した値を変数にセットすることができる。 最終行まで処理を終えた後はfetchでnot foundのハンドラが出るので、変数と組み合わせることでループ処理を抜けることができる。 カーソルを複数使って2重ループなども可能。ただし、ハンドラでセットするループを抜けるための変数を適切に初期化しないとうまく動作しないので注意。 トリガーの例 トリガーの使い道はいろいろ考えられるが、例えば履歴用のテーブルを作成して、テーブルの変更履歴を残す場合などに有効。 ここでは、ユーザー情報を管理するテーブルを用意し、メールアドレスが変更された履歴を残すトリガーを考える。 ユーザー情報のテーブルは以下の通り。 users id user_name email 1 AAA AAA@gmail.com 2 BBB BBB@gmail.com 3 CCC CCC@gmail.com メールアドレスが更新された場合の履歴を残す以下のような構造のテーブルを用意する。 email_update_log id user_id before_email after_email updated_at 1 1 before@gmail.com after@gmail.com 2021-11-01 トリガーは以下のようになる。 delimiter // -- drop trigger email_update_trigger // create trigger email_update_trigger after update on users for each row begin -- 更新前と更新後の値を比較する if old.email <> new.email then insert into email_update_log (user_id, before_email, after_email, updated_at) values (new.id, old.email, new.email, current_timestamp()); end if; end // delimiter ; 確認 -- データインサート insert into users(user_name, email) values('AAA', 'AAA@gmail.com'); select * from users; -- 確認 select * from email_update_log; -- 確認 最初はレコード無し -- メールアドレスを更新 update users set email = 'DDD@gmail.com' where user_name = 'AAA'; -- トリガーが動いているか確認 select * from email_update_log; 結果 id user_id before_email after_email updated_at 1 1 AAA@gmail.com DDD@gmail.com 2021-10-25 13:00:00 うまく登録できていれば、usersテーブルにupdateを実行したタイミングでレコードが増えることが確認できる。 解説 トリガーの概要 usersテーブルにupdateが行われたときに、更新前と更新後のemailを比較して値が変わっていた場合に履歴用テーブルにデータを更新する。 宣言部 email_update_triggerがトリガー名 afterは更新後の処理を行うことを表す。更新前に処理を走らせたい場合はbeforeを指定する。 updateは更新の種類。insert, update, deleteのいずれかが選択できる。 on usersでトリガーとなるテーブルを指定する。 for each rowは、トリガーが更新された行毎に動作することを表す。他のDBMSではSQL文単位でトリガーを実行することができる仕組みもあるが、残念ながらMySQLではサポートされていないため、for each rowしか指定できない。 oldとnewの補足 今回の例ではoldとnewというキーワードを使って更新前と更新後の値を比較したが、oldとnewの2つが同時に使えるのはupdateのトリガーのみ。 insertのトリガーではnewのみ、deleteのトリガーではoldのみが使用可能。updateだけoldとnewの2つが使用可能。 トリガーの注意点 トリガーは使いようによっては便利だが、トランザクション制御が行われている中の1つのテーブルに対してトリガーを定義するときには注意が必要。 例えば、以下のような処理があったとする。 start transaction; -- tableAの更新 -- tableBの更新 -- tableCの更新 commit; そしてtableBに対してトリガーが定義されていたとする。 このとき、トリガーの中ではtableBよりも前に更新されたtableAの値は参照することができるが、tableBより後に更新されているtableCの値は参照することができない。 create trigger tableb_trigger after update on tableB for each row begin -- tableA 更新後のレコードを参照可能 -- tableB 更新前と更新後の値を参照可能 -- tableC 更新後のレコードは参照できない end つまり、トランザクション制御されていたとしても、コミット後のデータを全て参照できるわけではなく、tableBのトリガーではあくまでもtableBの値が更新されたタイミングで反映された値のみが参照可能。 勝手な思い込みでコミットされたタイミングでトリガーが走るのかなと思っていましたが、そうではなく、トランザクションの中でtableBの更新にトリガーが不随して処理されるイメージで考えたほうが良さそう。 まとめ MySQLのストアドは最近になって初めて使いましたが、意外と色々できるなと思いました。(失礼。) MySQL自体が業務で使用する機会がほぼなかったこともありますが、機能がしょぼいイメージが強かったので、ストアドが普通にちゃんとしていることにびっくりしました。(かなり失礼。) とはいえ、冒頭にも書いたけどOracleやSQL Serverなどの有償のDBMSに比べればできることはかなり少ない。 状況によっては結構便利ではあるけれど、大規模なシステムでMySQLのストアドを多用するのはやはりおすすめしない。 システムの機能の一部でプロシージャやトリガーを使った方がスマートに作れると感じる場合には部分的に使うのは大いにありかと。 ※個人的にSQL全部小文字で書いちゃう派なので、人によっては読みずらいかも。。大文字でも小文字でも動作するので好きな方で読み替えて下さい。 追記 ストアド(ファンクション、プロシージャ、トリガーのいずれか)を登録するときに、権限のエラーが出ることがある。 ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) このエラーが出たときの解決策は2つある。 ユーザーにsuper権限を与える log_bin_trust_function_creatorsグローバルシステム変数を1にする 1で解決する場合 権限を確認するには以下のSQLを実行 select user, Super_priv from mysql.user; ログインしている該当ユーザーのSuper_privが「Y」になっていればsuper権限があり、「N」の場合は権限がない状態。 権限を付与するにはUPDATE文を実行する。 update mysql.user set Super_priv='Y' where user='ユーザー名'; 2で解決する場合 システム変数の値を確認するには以下のSQLを実行 show variables like 'log_bin_trust_function_creators'; valueがONになっていれば有効で、OFFになっていれば無効。 ONにするには以下を実行する。 set global log_bin_trust_function_creators = 1; いずれかの方法で登録ができるようになるはずです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SQL】mysqlでサブクエリを実行する

入力データ ※データの作成は一番下を参照 実行文 select id,name,price from food_data main where price = (select max(price)from food_data sub where main.category = sub.category); ↓これでもできる SELECT id,name,max(price) FROM food_data GROUP BY category; 1・カテゴリがフルーツ同士で、今の値段が最大値(20)だったらレコードを取得しに行く。 2・同上 3・カテゴリが野菜同士で、今の値段が最大値(100)だったらレコードを取得しに行く。 4・同上 結果 ※フルーツと野菜の一番多い個数を取得できた データ作成 CREATE TABLE `food_data` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `category` varchar(255) DEFAULT NULL, `number` int(10) DEFAULT '0', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2423 DEFAULT CHARSET=utf8mb4;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【2021年度10月版】Django REST frameworkをDockerで動かす(Pipenv、MySQL、pre-commit導入)

皆さん一度は聞いたことがあるであろうPythonの代表的なフレームワークDjango。 先週ぐらい前に初めて触れたのですが、開発効率がとんでもなく上がりますね。 なんといってもREST APIの構築がかなり簡単に行なえますし、今までAPIの構築をAPI GatewayやLambdaで構築していたのが時間の無駄だったのではないかと思うようになりました。 MySQL、Oracle、PosgreSQLなどのデータベースも豊富用意されていますし、今後はサーバーサイドの開発はDjangoで開発することが増えてきそうですね。 そんなDjangoをDockerで動かしてみようという記事になります。 私自身、Djangoに触れてからわずか1週間しか経っておらず、普段はフロントエンドを中心に開発しておりますので、記事の内容に不備がある可能性がございますのでその点はご了承下さい。 注意事項 Python2系はどうやらサポートが終了してるそうなので。、Python3系を使用します。 データベースはMySQLを使用しますが、他のデータベースを使用する場合はデータベースの設定の部分だけ適時変えて下さい。(インストールするライブラリとデータベースの設定ファイルを変えるだけなので、そこを変えたからと言ってエラーが出るということはほぼ無いと思います。) Macでの環境を前提に話を進めていきます。 APIやDBの設定の説明などは、既に記事を出している方も多いので省きます。 また、筆者の環境は以下になります。 項目 バージョン OS Big Sur v11.6(M1チップ) python3 3.9.7 pip 21.3 dokcer 20.10.8 それでは、多少長めの記事となっておりますが頑張っていきましょう! 事前準備 DjangoをDockerで動かす前に、まずは、必要なものをインストールしておきましょう。 Python・Pipコマンド確認 バージョン確認などでPython3コマンドとPipコマンドが使えるかの確認。 $ python3 --version $ pip --version Docker Desktop Docker専用のデスクトップアプリをダウンロードして下さい。 ただし、プロセッサーがIntelの場合とM1の場合でダウンロードするファイルが違うので、下記のアドレスを参考に今一度ご自身のプロセッサーをご確認下さい。 https://helpx.adobe.com/jp/download-install/kb/apple-silicon-m1-chip.html 下記のリンクから自分の環境に合ったDokcerをインストール。 プロセッサー バージョン Intel Mac with Intel chip M1 Mac with Apple chip https://docs.docker.com/desktop/mac/install/ VSCode拡張機能 拡張機能もインストール。 ・https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker Dockerはコンテナにアクセスするときに、長いコマンドを打つ羽目になるので上記の拡張機能をインストールしておくことをオススメします。 MySQL DjangoでMySQLを使用する場合mysqlclientというライブラリを使用するのですが、MySQLライブラリをインストールしていないとエラーが出るためインストール。 $ brew install mysql Pipenv プロジェクトごとにライブラリを管理するためにPipenvをインストール。 Nodeでいうnpmやyarnのことです。 $ brew install pipenv $ pipenv --version Django環境構築 それでは本題のDjangoをDockerで動かしていきましょう。 Docker Desktopを立ち上げた上で行って下さい。(dockerコマンドが打てないため) 必要なライブラリのインストール $ pipenv install django $ pipenv install djangorestframework $ pipenv install django-filter Docker設定ファイル $ touch Dockerfile docker-compose.yml Dockerfile FROM python:3.9 ENV PYTHONUNBUFFERED 1 WORKDIR /code ADD Pipfile* /code/ RUN cd /code && \ pip install -U pip && \ pip install pipenv && \ pipenv install --system --ignore-pipfile --deploy COPY . /code/ docker-compose.yml docker-compose.yml version: '3' services: db: platform: linux/x86_64 image: mysql:8.0 container_name: example_db environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: example-db MYSQL_USER: example MYSQL_PASSWORD: example-password TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci web: build: . container_name: example_web_app command: python3 manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - db Djangoプロジェクト作成 $ docker-compose run web django-admin.py startproject example . この場合、exampleディレクトリが作成され、このディレクトリを起点に設定などを行っていきます。 MySQL設定 DjangoはデフォルトではSQLiteが使われているので、これをMySQLに変更。 example/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'example-db', 'USER': 'example', 'PASSWORD': 'example-password', 'HOST': 'db', 'PORT': '3306' } } docker-compose.ymlと値を一致させて下さい。 docker-compose.yml settings.py MYSQL_ROOT_PASSWORD なし MYSQL_DATABASE NAME MYSQL_USER USER MYSQL_PASSWORD PASSWORD ローカルサーバー立ち上げ ビルドして立ち上げます。(Pipenvで動かしているため少し時間がかかります) $ docker-compose up -d --build そして、http://localhost:8000へアクセス。 もしローカルサーバーが立ち上がらない場合は、VSCodeの左側にDockerのマークがあると思うのでそこをクリック。(拡張機能をインストールしないと表示されない) そこで、mysqlじゃない方を右クリックし「Restart」を選択。 するとローカルサーバーが起動します。 MySQLにアクセス VSCodeの左側のDockerのマークをクリック。 そのマークをクリックし、「mysql:8.0」で右クリック → 「Attch Shell」を選択。 $ mysql -u root -p > Enter password: docker-compose.ymlのMYSQL_ROOT_PASSWORDの値を入力 MySQLにアクセスできればOK。 試しにデータベースの一覧を表示してみましょう。 mysql > show databases; > docker-compose.ymlのMYSQL_ROOT_PASSWORDの値を入力 +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | example-db | | sys | +--------------------+ 5 rows in set (0.02 sec) これでDjangoをDockerで動かすことが出来ましたね。 独自スクリプト・pre-commit導入 最後に快適な環境を手に入れるために独自スクリプトの追加とpre-commitを導入していきます。 独自スクリプト VueやReactで開発するときってローカルサーバーを立ち上げる場合、npm run startなどのコマンドを打ちますよね。 それらはpackage.jsonで定義されているのですが、それと同様にPipenvを使えば同じようにローカルサーバーを立ち上げたり、ビルドなども行えます。 現にビルドコマンドを追加してみます。 ディレクトリ直下のPipfileに[scripts]を追加。 [scripts] build = "docker-compose up -d --build" そしたら、pipenv runでこのコマンドを打ってみます。 $ pipenv run build すると、先程と同じようにビルドが開始されます。 あとは自分の好きなようにカスタマイズしてみて下さい! pre-commit pre-commitを導入する前に、まずはflake8とautopep8を導入します。 flake8 falke8とはPython版のESLintです。 とりあえずインストール。 $ pipenv install --dev flake8 flake8をスクリプトで走らせてみましょう。 Pipfileのscriptsに追加。 [scripts] build = "docker-compose up -d --build" lint = "flake8 ." $ pipenv run lint autopep8 お次はautopep8(Python版のPrettier)を導入しましょう。 $ pipenv install --dev autopep8 同じようにスクリプトを追加し、走らせてみる。 [scripts] build = "docker-compose up -d --build" lint = "flake8 ." format = "autopep8 -ivr ." $ pipenv run format また、PrettierやESLintと同様に自分好みの設定を施したい方は以下の記事が参考になります。 Pipenv で flake8 / autopep8 を上手く使う pre-commit導入 さて、最後にflake8とautopep8をGitでCommitする前に適用させるようにしていきましょう。 ちなみにpre-commitはPython版のHuskyみたいなものです。 $ brew install pre-commit $ pre-commit --version そしたら、設定ファイルの作成。 $ pre-commit sample-config > .pre-commit-config.yaml プロジェクト直下に.pre-commit-config.yamlが作成され、中身を以下に変更。 pre-commit-config.yaml # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://gitlab.com/pycqa/flake8 rev: 3.8.1 hooks: - id: flake8 args: [--ignore=E402, --max-complexity, "10", --max-expression-complexity=7, --max-cognitive-complexity=7] additional_dependencies: [flake8-bugbear, flake8-builtins, flake8-eradicate, pep8-naming, flake8-expression-complexity, flake8-cognitive-complexity] - repo: https://github.com/pre-commit/mirrors-autopep8 rev: v1.5.7 hooks: - id: autopep8 args: [--in-place, -r, --aggressive, --exclude, "manage.py,./*/migrations,./*/snapshots,./*,./**/__init__.py,hirameki/asgi.py", --max-line-length, "120"] 以下のコマンドでgitにhooksを追加。 $ pre-commit install これでコミットする前にflake8とautopep8が走ります。 実際にコミットし、プッシュしてみましょう。 $ git add . $ git commit -m 'pre-commit動作確認' $ git push いかがだったでしょうか? PipenvでPythonの環境構築を行ったのは今回が初めてなので、多少至らない部分もありますがその点はご了承下さい。 また、何か気になる点や間違っているところがあればコメント欄にて教えてくださると助かります。 エラーが出た場合もご気軽にご相談下さい! 以上、「【2021年度10月版】Django REST frameworkをDockerで動かす(Pipenv、MySQL、pre-commit導入)」でした! Thank you for reading 参考記事 以下、この記事を書くにあたって参考にさせていただいた記事となります。 ・2018年のPythonプロジェクトのはじめかた ・Django REST Frameworkを使って爆速でAPIを実装する ・Django+MySQLの開発環境をdocker-composeで構築する ・【Python】Pipfile.lockを活用したDockerとpipenvでの安全な環境構築 ・pre-commitでコミット時にコードの整形やチェックを行う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでMySQLに接続する時に対応したエラー

問題1 エラー1 Mysql2::Error: Host '172.22.0.1' is not allowed to connect to this MySQL server 対処方法 ユーザーに、外部からのアクセス権を付与する  手順 1、ユーザーのhostを確認 SELECT host, user, plugin FROM mysql.user; +------------------+-----------+ | user | host | +------------------+-----------+ | mysql.infoschema | localhost | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | | user   | localhost | +------------------+-----------+ 2、hostをlocalhost→%(ワイルドカード)に変更 CREATE USER 'user' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON * . * TO 'user'@'%' WITH GRANT OPTION; 3、再度hostの状態を確認して%になっていたらOK SELECT host, user, plugin FROM mysql.user; +------------------+-----------+ | user | host | +------------------+-----------+ | mysql.infoschema | localhost | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | | user | % | +------------------+-----------+ 問題2 エラー2 Unable to connect to host 127.0.0.1, or the request timed out. Be sure that the address is correct and that you have the necessary privileges, or try increasing the connection timeout (currently 10 seconds). MySQL said: Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(/usr/local/mysql/lib/plugin/caching_sha2_password.so, 2): image not found 対処方法 caching_sha2_password -> mysql_native_password に変更 手順 1、認証プラグインを確認 select user, host from mysql.user; +-----------+------------------+-----------------------+ | host | user | plugin | +-----------+------------------+-----------------------+ | % | user          | caching_sha2_password | | localhost | mysql.infoschema | caching_sha2_password | | localhost | mysql.session | caching_sha2_password | | localhost | mysql.sys | caching_sha2_password | | localhost | root | caching_sha2_password | +-----------+------------------+-----------------------+ 2、pluginをmysql_native_passwordに変更 ALTER USER 'user'@"localhost" IDENTIFIED WITH mysql_native_password BY 'password'; 3、再度pluginの状態を確認してmysql_native_passwordになっていたらOK select user, host from mysql.user; +-----------+------------------+-----------------------+ | host | user | plugin | +-----------+------------------+-----------------------+ | % | user          | mysql_native_password | | localhost | mysql.infoschema | caching_sha2_password | | localhost | mysql.session | caching_sha2_password | | localhost | mysql.sys | caching_sha2_password | | localhost | root | caching_sha2_password | +-----------+------------------+-----------------------+ 問題3 エラー3 SQLSTATE[HY000] [2002] Connection refused 対処方法 hostをコンテナ内のIPアドレスにする 同じDocker内の通信なのでlocalhost(127.0.0.1)でいいと思っていたのですが、コンテナでIPアドレスが異なるみたいです 手順 1、DBコンテナに接続してIPアドレスを確認 hostname -i root@b3179d7e8745:/# hostname -i 172.19.0.4 2、1で確認したhostを設定する $db = new PDO('mysql:host=172.19.0.4;dbname=db_name', $user, $pass);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む