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

Docker 初心者が Mattermost を docker-compose で立ち上げる話

はじめに

オンプレ型チャットツール「Mattermost」のプラグインを開発するために、
Mattermost サーバーを立てようとしていたのですが、

  • 初心者だけど Docker で楽に構築したい
  • Enterprise Edition を試したい
  • バージョン(5.27.0)を指定したい

という条件下でかなり苦戦してしまいました。
自分のために、そして同じ状況下で困っている方(がいるかわかりませんが)のために、
他に参考にした記事を引用しつつ、どんな流れで構築したかを記録しておきます。

前提

目指す環境

  • Mattermost Enterprise Edition(5.27.0)が動作する
  • Mattermost の DB には MySQL(8.0)を使用する
  • Mattermost まわりのあれこれを Docker で起動する
  • (おまけ)Mattermost のプラグインがファイルでアップロードできる

自分の環境

  • macOS Catalina 10.15.6
  • Docker
    • Desktop 2.3.0.4
    • Server Engine 19.03.12
    • Client Engine 19.03.12
  • Git 2.24.3 (Apple Git-128)

わたし

タイトルにもある通り、Docker ド初心者 です。

  • かろうじて自分のMacにDockerが入っている
  • docker run docker stop docker rm はとりあえず使える
  • めっちゃ頑張ったら起動中のコンテナのシェルに入れる
  • Docker イメージはなんとなくわかる
  • Dockerfile を雰囲気で読みながら生きてきた
  • docker-compose ってなんなん?

というレベルです。優しくしてください。

構築

というわけで、やってみましょう。

1. Mattermost-docker を用意する

今回は、Mattermost サーバーの設定を Docker で行うために、
以下のリポジトリをローカルにcloneします。

git clone https://github.com/mattermost/mattermost-docker.git

README を読んでみると、
「これ使うと、Mattermost サーバーを Docker 使って簡単にデプロイできるよ(意訳)」と。
なるほど...

2. docker-compose.yml を読んでみる

こちらの記事(Mattermost+MySQLのセットアップ (Docker) - Qiita)を参考に、
docker-compose.yml とやらを書き換えてみたいんですが、
その前に「そもそもこれなんやねん」ということで、ちょっと中身を覗いてみます。

docker-compose.yml
version: "3"

services:

  db:
    build: db
    read_only: true
    restart: unless-stopped
    volumes:
      - ./volumes/db/var/lib/postgresql/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=mmuser
      - POSTGRES_PASSWORD=mmuser_password
      - POSTGRES_DB=mattermost
    # uncomment the following to enable backup
    #  - AWS_ACCESS_KEY_ID=XXXX
    #  - AWS_SECRET_ACCESS_KEY=XXXX
    #  - WALE_S3_PREFIX=s3://BUCKET_NAME/PATH
    #  - AWS_REGION=us-east-1

  app:
    build:
      context: app
      # uncomment following lines for team edition or change UID/GID
      # args:
      #   - edition=team
      #   - PUID=1000
      #   - PGID=1000
    restart: unless-stopped
    volumes:
      - ./volumes/app/mattermost/config:/mattermost/config:rw
      - ./volumes/app/mattermost/data:/mattermost/data:rw
      - ./volumes/app/mattermost/logs:/mattermost/logs:rw
      - ./volumes/app/mattermost/plugins:/mattermost/plugins:rw
      - ./volumes/app/mattermost/client-plugins:/mattermost/client/plugins:rw
      - /etc/localtime:/etc/localtime:ro
      # When you want to use SSO with GitLab, you have to add the cert pki chain of GitLab inside Alpine
      # to avoid Token request failed: certificate signed by unknown authority (link: https://github.com/mattermost/mattermost-server/issues/13059)
      # - <path_to_your_gitlab_pki>/pki_chain.pem:/etc/ssl/certs/pki_chain.pem:ro
    environment:
      # set same as db credentials and dbname
      - MM_USERNAME=mmuser
      - MM_PASSWORD=mmuser_password
      - MM_DBNAME=mattermost

      # use the credentials you've set above, in the format:
      # MM_SQLSETTINGS_DATASOURCE=postgres://${MM_USERNAME}:${MM_PASSWORD}@db:5432/${MM_DBNAME}?sslmode=disable&connect_timeout=10
      - MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:mmuser_password@db:5432/mattermost?sslmode=disable&connect_timeout=10

      # in case your config is not in default location
      #- MM_CONFIG=/mattermost/config/config.json

  web:
    build: web
    ports:
      - "80:80"
      - "443:443"
    read_only: true
    restart: unless-stopped
    volumes:
      # This directory must have cert files if you want to enable SSL
      - ./volumes/web/cert:/cert:ro
      - /etc/localtime:/etc/localtime:ro

どうやらdb app web の三層構造になってるようです。
この三層構造は別に Mattermost 特有の何かとかではなく、
一般的な Web三層アーキテクチャです。
その構造に沿って docker-compose.yml が記述されていると。

これまで docker run するたびに Apache だの SQL だの一個ずつ起動してたけど、
どうやら階層構造をちゃんと決めて、
アプリケーションに関連するものをまとめて起動してくれそうだぞ、という雰囲気がします。

3. docker-compose.yml を書き換える

なんとなく構成がわかったところで、目指す環境にあうように中身を書き換えていきます。
手っ取り早く進めたい方は以下のソースコードをコピペして、次のステップへ進んでください。

docker-compose.yml
docker-compose.yml
version: "3"

services:

  db:
    image: mysql/mysql-server:8.0
    # read_only: true
    restart: unless-stopped
    volumes:
      # - ./volumes/db/var/lib/postgresql/data:/var/lib/postgresql/data
      - ./volumes/db/var/lib/mysql:/var/lib/mysql
      - ./volumes/db/etc/my.cnf.d:/etc/my.cnf.d
      # - /etc/localtime:/etc/localtime:ro
    environment:
       - MYSQL_ROOT_PASSWORD=password
       - MYSQL_USER=mmuser
       - MYSQL_PASSWORD=password
       - MYSQL_DATABASE=mattermost
       - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`
    ports:
       - "33306:3306"
    # uncomment the following to enable backup
    #  - AWS_ACCESS_KEY_ID=XXXX
    #  - AWS_SECRET_ACCESS_KEY=XXXX
    #  - WALE_S3_PREFIX=s3://BUCKET_NAME/PATH
    #  - AWS_REGION=us-east-1

  app:
    build:
      context: app
      # uncomment following lines for team edition or change UID/GID
      args:
         - edition!=team
      #   - PUID=1000
      #   - PGID=1000
    restart: unless-stopped
    volumes:
      - ./volumes/app/mattermost/config:/mattermost/config:rw
      - ./volumes/app/mattermost/data:/mattermost/data:rw
      - ./volumes/app/mattermost/logs:/mattermost/logs:rw
      - ./volumes/app/mattermost/plugins:/mattermost/plugins:rw
      - ./volumes/app/mattermost/client-plugins:/mattermost/client/plugins:rw
      # - /etc/localtime:/etc/localtime:ro
      # When you want to use SSO with GitLab, you have to add the cert pki chain of GitLab inside Alpine
      # to avoid Token request failed: certificate signed by unknown authority (link: https://github.com/mattermost/mattermost-server/issues/13059)
      # - <path_to_your_gitlab_pki>/pki_chain.pem:/etc/ssl/certs/pki_chain.pem:ro
    environment:
      # set same as db credentials and dbname
      - MM_USERNAME=mmuser
      - MM_PASSWORD=mmuser_password
      - MM_DBNAME=mattermost
      - DB_PORT_NUMBER=3306
      - MM_SQLSETTINGS_DRIVERNAME=mysql
      - MM_SQLSETTINGS_DATASOURCE=mmuser:password@tcp(db:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s
      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`
      # use the credentials you've set above, in the format:
      # MM_SQLSETTINGS_DATASOURCE=postgres://${MM_USERNAME}:${MM_PASSWORD}@db:5432/${MM_DBNAME}?sslmode=disable&connect_timeout=10
      # - MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:mmuser_password@db:5432/mattermost?sslmode=disable&connect_timeout=10

      # in case your config is not in default location
      #- MM_CONFIG=/mattermost/config/config.json

  web:
    build: web
    ports:
      - "8080:80"
      - "8443:443"
    read_only: true
    restart: unless-stopped
    volumes:
      # This directory must have cert files if you want to enable SSL
      - ./volumes/web/cert:/cert:ro
      # - /etc/localtime:/etc/localtime:ro
    environment:
      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`

db

まずは MySQL を使うために、db の部分を書き換えます。

docker-compose.yml
   db:
-    build: db
-    read_only: true
+    image: mysql/mysql-server:8.0
+    # read_only: true
     restart: unless-stopped
     volumes:
-      - ./volumes/db/var/lib/postgresql/data:/var/lib/postgresql/data
+      # - ./volumes/db/var/lib/postgresql/data:/var/lib/postgresql/data
+      - ./volumes/db/var/lib/mysql:/var/lib/mysql
+      - ./volumes/db/etc/my.cnf.d:/etc/my.cnf.d
-      - /etc/localtime:/etc/localtime:ro
+      # - /etc/localtime:/etc/localtime:ro
     environment:
-      - POSTGRES_USER=mmuser
-      - POSTGRES_PASSWORD=mmuser_password
-      - POSTGRES_DB=mattermost
+      - MYSQL_ROOT_PASSWORD=password
+      - MYSQL_USER=mmuser
+      - MYSQL_PASSWORD=password
+      - MYSQL_DATABASE=mattermost
+      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`
+    ports:
+       - "33306:3306"

なお、DB 関連のパスワードは password に設定しています。
よろしくないパスワードですが、今回はあくまで動作確認用なので目をつむります。
(気になる方はもちろん変更してください)

また、以下の部分は Docker のマウント失敗を回避するためのものです。

     volumes:
               ...(中略)...
-      - /etc/localtime:/etc/localtime:ro
+      # - /etc/localtime:/etc/localtime:ro

     environment:
               ...(中略)...
+      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`

app と web でも同様の設定が必要です。
(参考:Dockerで- /etc/localtime:/etc/localtime:ro がMount Deniedを出すやつ - Steady WorX はてな

app

Enterprise Edition を使うために、edition を設定します。
また、先ほど設定した DB の設定に合うように各種ポート番号などを書き換えます。

docker-compose.yml
  app:
    build:
       context: app
       # uncomment following lines for team edition or change UID/GID
-      # args:
-      #   - edition=team
+      args:
+         - edition!=team
       #   - PUID=1000
       #   - PGID=1000
    restart: unless-stopped
    volumes:
      - ./volumes/app/mattermost/config:/mattermost/config:rw
      - ./volumes/app/mattermost/data:/mattermost/data:rw
      - ./volumes/app/mattermost/logs:/mattermost/logs:rw
      - ./volumes/app/mattermost/plugins:/mattermost/plugins:rw
      - ./volumes/app/mattermost/client-plugins:/mattermost/client/plugins:rw
+      # - /etc/localtime:/etc/localtime:ro
    environment:
      - MM_USERNAME=mmuser
      - MM_PASSWORD=mmuser_password
      - MM_DBNAME=mattermost
+      - DB_PORT_NUMBER=3306
+      - MM_SQLSETTINGS_DRIVERNAME=mysql
+      - MM_SQLSETTINGS_DATASOURCE=mmuser:password@tcp(db:3306)/mattermost?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s
+      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`

       # use the credentials you've set above, in the format:
       # MM_SQLSETTINGS_DATASOURCE=postgres://${MM_USERNAME}:${MM_PASSWORD}@db:5432/${MM_DBNAME}?sslmode=disable&connect_timeout=10
-      - MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:mmuser_password@db:5432/mattermost?sslmode=disable&connect_timeout=10
+      # - MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:mmuser_password@db:5432/mattermost?sslmode=disable&connect_timeout=10

       # in case your config is not in default location
       #- MM_CONFIG=/mattermost/config/config.json

web

最後に web も少しだけ書き換えます。
ポート番号の設定変更くらいですね。

docker-compose.yml
   web:
     build: web
     ports:
-      - "80:80"
-      - "443:443"
+      - "8080:80"
+      - "8443:443"
     read_only: true
     restart: unless-stopped
     volumes:
       # This directory must have cert files if you want to enable SSL
       - ./volumes/web/cert:/cert:ro
-      - /etc/localtime:/etc/localtime:ro
+      # - /etc/localtime:/etc/localtime:ro
+    environment:
+      - TZ=`ls -la /etc/localtime | cut -d/ -f8-9`

4. バージョンを指定する

今回は 5.27.0 のバージョンで動作させたかったので、
バージョンを指定するために環境変数を設定します。
mattermost-server/app/Dockerfile をみてみると、

mattermost-server/app/Dockerfile
# Some ENV variables
ENV PATH="/mattermost/bin:${PATH}"
ENV MM_VERSION=5.27.0
ENV MM_INSTALL_TYPE=docker

バージョン指定のための環境変数があることがわかります。
他のバージョンに変更したい方は、MM_VERSIONの値を好きに変えてください。
デフォルトは 5.27.0 のようです。

5. マウント用ディレクトリの作成

mkdir -pv ./volumes/app/mattermost/{data,logs,config,plugins,client-plugins}

先にマウント用ディレクトリを用意しておきます。
これをすっ飛ばすとあとで怒られます。

6. いざ、docker-compose up!

docker-compose up -d --build

お、なんだかできてそうな出力結果...!

Successfully tagged mattermost-docker_web:latest
Creating mattermost-docker_app_1 ... done
Creating mattermost-docker_db_1  ... done
Creating mattermost-docker_web_1 ... done

先ほど設定したポートは 8080 だったので、
http://localhost:8080 を開いてみましょう。

Screenshot 2020-10-15 21.51.26.png

おおー!ちゃんと表示されました!よかったよかった。

7. 停止

立ち上げたコンテナは止めましょう。ということで

docker-compose stop

すれば、コンテナを止めることができます。

まとめ

というわけで、本記事では、docker-compose.ymlを書き換えながら
Mattermost を三層構造のWebアプリケーションとして立ち上げました。
同じように環境構築で困っている人や、未来の自分に役立つ記事になっていれば嬉しいです。

個人的には、docker-compose.yml まじでわからん、から
何書くもんなんか大体分かったわ、くらいにはなれたのが収穫です。物は試し。

おまけ:プラグイン開発したい人向け

今回はプラグイン開発環境するぞー!ってことでこの環境を作ったわけですが、
プラグインをシステムコンソールからアップロードしようとすると
Screenshot 2020-10-15 21.53.41.png
アップロードボタンが灰色になっている....

というわけで、起動中のコンテナ(app)に入って書き換えました。
私は Docker Desktop から CLI を起動しちゃいましたが、

docker ps

で app の ID を調べて、

docker exec -it <appのID> /bin/sh

としたらコンテナに入れますね。

書き換えが必要なのは以下の部分。

app/config/config.json
   "PluginSettings": {
     "Enable": true,
-    "EnableUploads": false,
+    "EnableUploads": true,
     "Directory": "./plugins",
     "ClientDirectory": "./client/plugins",
     "Plugins": {},

なんでデフォルトは false なんでしょうね...
なにはともあれ、これで解決。

Screenshot 2020-10-15 22.07.20.png

アップロード用のボタンが現れ、アップロードも無事に出来ました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS - RDS】MySQLに特定のDBのみのアクセスユーザー作成

1つのDBサーバーを使用して、複数のDBを管理している場合、
特定のDBへのみアクセス出来るユーザーを作成したい。

RDSにアクセス

アクセス権のあるEC2から下記を実行。
※ RDSへアクセス権があることを確認。セキュリティグループ等

$ mysql -h RDS_ENDPOINT -u root -p
Enter password: パスワードを入力

ユーザーの作成

ALL で、全ての権限を付与。
DATABASE_NAME.* で、全てのテーブルを対象とする。
USERNAME@% で、全てのホストを対象とする。

$ GRANT ALL ON DATABASE_NAME.* TO 'USERNAME'@'%' IDENTIFIED BY 'YOUR_PASSWORD';

USERNAME が存在しない場合は、新規でユーザーを作成

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データベースの文字化けを直す

フォームなどを作っているとアルファベットはいいのですが、日本語投稿が?になってしまいます。その改善方法をまとめました。

データベースの文字コードをチェック

show variables like ‘character%’;

を実行してみてください。するとutf8に混じってlatin1が出てこないでしょうか。

データベースの文字コードを修正

set character_set_database=utf8;

こんな感じでlatin1のものをutf8に変更していきます。

テーブルの文字コードを修正

alter table table_name convert to character set utf8;

以上を試した上で、もう一度日本語の投稿をしてみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RustとMySQLで簡単なWebApplicationを実装する

Rustで業務アプリケーションを構築中の駆け出しプログラマーです。
今回は練習のためRustで簡単なWebアプリを実装しようと思います。
概要としては、WebからPOST/GETしてMySQLへ書き込みしたり読み出したり
します。

正直なところ細かいところまで調べきれていないため勉強用記事というようりは備忘録になっています。随時新しい知識が身に付けば更新しまする予定ですのでお手柔らかににお願いします。

元の記事

前提

各種環境は以下の通りです。エディタはVSコードでRustAnalyzerという
ツールを入れています。

MacOS
cargo 1.46.0 
rustc 1.46.0
MySQL  Ver 8.0.21

プロジェクトの作成

% cargo new web_app
     Created binary (application) `web_app` package

% cd web_app 

actix-webの導入

actixについては以下、引用させていただきました。

actixとは、Rust製のActorフレームワークです。
(Java/Scalaでいうと、Akkaがメジャーですね)
アクター同士が非同期でメッセージをやり取りし、安全に並行処理を行うことができます。
そのactixをベースとしてWeb開発用機能を追加したのが、
軽量・高速なWeb開発フレームワークであるactix-webです。
actix-webで開発されたアプリは、実行ファイルにHTTPサーバーを含んでいるため、
そのまま使うこともできるし、apacheやnginxの後ろに置くこともできます。 
参照元

cargo.tomlファイルにactix-webを使用することを宣言します。

[dependencies]
actix-web = "3.1.0"
actix-rt = "1.1.1"

次に、actixを利用してウェブページを返えすためのソースを書きたいところですが今回はMVC的な感じのディレクトリ構成にして実装を進めたいため先にディレクトリを整理します。
以下のような構成にしてください。
ちなみに、ディレクトリ構成についてはこちらのサイトを参考にさせていただきました。

web_app
├── Cargo.lock
├── Cargo.toml
├── src
│   ├── controllers
│   │   ├── index.rs
│   │   └── mod.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── models
│   └── routes.rs
└── target

Webページを見れるようにする

それでは早速、main.rsをいじります。
ここら辺についてはこちらを参考にさせていただきました。
参考記事ではroutesというファイルは存在しないのですが、今回はMVC的な感じにするためroutesファイルを作成してルーティングを行います。
この時、routesを利用するよ、というuse宣言を記述し忘れないように気をつけましょう。

main.rs
use actix_web::{App, HttpServer};
use web_app::routes;

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().configure(routes::routes))
        .bind("localhost:8000")?
        .run()
        .await
}

routes.rsは以下の通りです。
注意点は、routeの最後の()後に「;」が必要なのは常に一番最後の行のrouteになります。これを記述し忘れてはまったので備忘録として。

routes.rs
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use crate::controllers::index;

pub fn routes(cfg: &mut web::ServiceConfig) {
    cfg.route("/", web::get().to(index::index));
}

次にlib.rsにモジュールを定義します。
これはroutesとcontrollersを外部参照できるようにするためです。

lib.rs
pub mod routes;
pub mod controllers;

次にcontrollersフォルダ内のmod.rsを以下の通り。

mod.rs
pub mod index;

index.rsを以下の通り。

index.rs
use actix_web::{web, HttpRequest, HttpResponse, Responder};

pub async fn index() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

ここでcargo runを実行して、[localhost:8000]に接続すると
WEBページが帰ってくるはずです。

Postmanの導入

DBへの接続へ進む前に、Get/Postメソッドを簡単に送ることができる便利ツールPostmanの導入をします。

https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=ja&pv

上記のサイトへいき、chromeの拡張機能へ登録してください。
その後アカウントを登録して、アプリを起動します。
chrome以外での使い方は調べていないので、追加で情報が必要な場合は公式サイトをご覧になると良いかと思います。
[公式サイト]

ためしにcargo runをしてサーバーを立ち上げた後に、postmanから
localhost:8000/へGETメソッドを送信するとしっかりとHello worldが帰ってきます。

スクリーンショット 2020-10-14 8.44.17.png

DBへ接続

今回はMySQLへ接続してGet/PostしたいのでMySQLにテスト用データベースを用意します。

MySQLの起動

% mysql.server start

MySQLへ接続
私の場合はパスワードつけてますが、おそらくrootユーザーでパスワード設定していない場合は-pがいらない、かもしれないです。
こちらに関しては各人の設定次第ですね。

 % mysql -u[ユーザー名] -p

テスト用のDBを作成
DBの名前は任意です。私はrust_web_testにしました。

% create database rust_web_test

ひとまずMySQはこれでOKです。次にRustのORM「Diesel」をインストールしていきます。
Dieselのインストールに関しては公式のチュートリアルを参考にしました。
まずは依存関係を定義します。

cargo.toml
[dependencies]
actix-web = "3.1.0"
actix-rt = "1.1.1"
diesel = { version = "1.4.4", features = ["mysql"] }
dotenv = "0.15.0"

次に、web_app配下に.envファイルを作成してdieselをインストールしましょう。.

cargo install diesel_cli

先ほど作成した.envファイルに今回使用するDBへの接続情報を記述します。

DATABASE_URL=mysql://ユーザー名:パスワード@localhost/rust_web_test

次に以下を実行。
これでweb_app配下にmigrationフォルダが作成されここでschemaをマネジメントしていきます。

diesel setup

試しにテーブルを作成してみましょう。
以下を実行するとmigrationフォルダの下に新しいフォルダが作成され、その配下に
・up.sql
・down.sql
が作成されます。

% diesel migration generate users 

up.sqlとdown.sqlにSQLを記述

up.sql
CREATE TABLE users
(
    id SERIAL PRIMARY KEY,
    name VARCHAR(64) NOT NULL
);
down.sql
DROP TABLE users;

ターミナルで以下を実行して変更を反映させます。これでSQLは以上です。

% diesel migration run
Running migration 2020-10-14-003024_users

% diesel migration redo
Rolling back migration 2020-10-14-003024_users
Running migration 2020-10-14-003024_users

これでshema.rsには以下のようなソースが追加されます。

schema.rs
table! {
    users (id) {
        id -> Unsigned<Bigint>,
        name -> Varchar,
    }
}

試しに、MySQLに接続してテーブルを確認すると「users」テーブルが作成されているはずです。

//接続
% mysql -uユーザー名 -p

//use
mysql> use rust_web_app

//show
mysql> show tables;
+----------------------------+
| Tables_in_rust_web_app     |
+----------------------------+
| __diesel_schema_migrations |
| users                      |
+----------------------------+
2 rows in set (0.00 sec)

ここまでやると自動的に以下のようなディレクトリになっていると思います。
util.rsは自分で付け足しました。
ここにはMySQLへ接続するための関数を記述します。

web_app
├── Cargo.lock
├── Cargo.toml
├── diesel.toml
├── migrations
│   └── 2020-10-14-003024_users
│       ├── down.sql
│       └── up.sql
├── src
│   ├── controllers
│   │   ├── index.rs
│   │   └── mod.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── models
│   ├── routes.rs
│   ├── schema.rs
└── target

ついでにlib.rsを更新しておきましょう。

さらにファイルを追加していきます。
・util.rs
・models/mod.rs
・models/users.rs
・controllers/create_user
以下のようなディレクトリになってればOKです。

web_app
├── Cargo.lock
├── Cargo.toml
├── diesel.toml
├── migrations
│   └── 2020-10-14-062158_users
│       ├── down.sql
│       └── up.sql
├── src
│   ├── controllers
│   │   ├── create_user.rs
│   │   ├── index.rs
│   │   └── mod.rs
│   ├── lib.rs
│   ├── main.rs
│   ├── models
│   │   ├── mod.rs
│   │   └── users.rs
│   ├── routes.rs
│   └── schema.rs 
│   └── util.rs
├── target
├── .env

まずは各ファイルを外部参照できるように、lib.rsを以下のようにします。

lib.rs
#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod routes;
pub mod controllers;
pub mod models;
pub mod schema;
pub mod util;

DBに値を書き込む

controllers配下のmod.rsに以下を追記します。

pub mod index;
pub mod create_user;

次にmodels配下のmod.rsに以下を追記

pub mod users;

create_user.rsに戻って以下を追記してください。
今回は「name」だけをDBに書き込みます。

create_user.rs
extern crate diesel;

use crate::models::users::User;
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct UserData {
    name: String,
}

pub async fn create(item: web::Json<UserData>) -> HttpResponse {
    let user = User::create(&(item.name));
    println!("{:?}",user);
    HttpResponse::Created().body("Inserting")

}

models/users.rsを以下の通り

users.rs
use diesel::prelude::*;
use crate::schema::users;
use crate::util::establish_connection;

#[derive(Debug, Queryable)]
pub struct User {
    pub id: u64,
    pub name: String,
}

#[derive(Insertable)]
#[table_name = "users"]
pub struct NewUser<'a> {

    name: &'a str,
}

impl User {
    pub fn all() -> Vec<User> {
        let connection = establish_connection();
        users::dsl::users
            .limit(30)
            .load::<User>(&connection)
            .expect("Error loading users")
    }

    pub fn create(name: &str) -> User {
        use self::users::id;
        let new_user = NewUser { name: name };
        let connection = establish_connection();
        diesel::insert_into(users::table)
            .values(&new_user)
            .execute(&connection)
            .expect("Error saving new user");
        users::dsl::users
            .order(id.desc())
            .first::<User>(&connection)
            .expect("Error finding users")
    }

}

次に、MySQLとのコネクションをするための関数をutilに記述します。

util.rs
use diesel::mysql::MysqlConnection;
use diesel::prelude::*;
use dotenv::dotenv;
use std::env;


pub fn establish_connection() -> MysqlConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    MysqlConnection::establish(&database_url)
        .expect(&format!("Error connecting to {}", database_url))
}

お次はcreate_userでシリアライズというアトリビュートを使用するので依存関係を定義します。

cargo.toml
[dependencies]
actix-web = "3.1.0"
actix-rt = "1.1.1"
diesel = { version = "1.4.4", features = ["mysql"] }
dotenv = "0.15.0"
serde = { version = "1.0.116", features = ["derive"] }
serde_json = "1.0.58"

最後にroutes.rsにcreate_userを追記して書き込みができるようになります。

routes.rs
extern crate diesel;

use crate::models::users::User;
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct UserData {
    name: String,
}

pub async fn create(item: web::Json<UserData>) -> HttpResponse {
    let user = User::create(&(item.name));
    println!("{:?}",user);
    HttpResponse::Created().body("Inserting")

}

cargo runを実行してpostmanでメソッドをGETからPOSTへ変更し、
アドレスをlocalhost:8000/create_userにしてください。
アドレスのすぐ下にあるタブに、「Headers」があるので
そこのKeyに「Content-Type」Valueに「application/json」と設定しましょう。

スクリーンショット 2020-10-14 14.13.33.png

次にBodyタブをクリックしてJson形式で値を入力してSendすれば値を書き込めます。

スクリーンショット 2020-10-14 14.16.19.png

ターミナルログ

Finished dev [unoptimized + debuginfo] target(s) in 30.12s
     Running `target/debug/web_app`
User { id: 2, name: "testuser1" }

MySQLに接続してテーブルの中身を確認すれば値が格納されているはずです。

DBの読み込み

次は格納した値をGetメソッドで呼び出してみたいと思います。

ルートにアクセスした際に格納した値を取り出す処理にしようと思いますので、
indexファイルを以下のように修正します。

index.rs
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use crate::models::users::User;

pub async fn index() -> impl Responder {
    let results = User::all();
    let mut res = format!("Displaying {} users\n\n", results.len());
    for user in results {
        let s = format!("id: {}, name: {}\n", user.id, user.name);
        res.push_str(&s);
    }

    HttpResponse::Ok().body(res)
}

終わりも近づいてきました。

PostmanでGetメソッドでルートにアクセスしてSendすると格納した値を取得できます。私は事前にtestuserを格納しているので値が2つありますね。
スクリーンショット 2020-10-14 14.31.21.png

これで今回の目的は達成しました。

ほとんど備忘録的な感じで解説をはさめていないため、随時更新して解説も入れていきたいと思います。(自分の勉強のため)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goでデータベースの内容を取得する関数を作る

データベースから目的の内容を全て取る関数について今から書きます。すでにデータベースには、なにか入っている状態でします

interface{} な変数を型が決まっている関数の引数にする
「Golang」 ×「Gorm」でシンプルに「Mysql」を操作する
【go + gin + gorm】webアプリにログイン機能を追加してみる
Goでの Mysql接続から構造体へのデータ割り当てまで
Gin と GORM で作るオレオレ構成 API


install
import (
    _ "github.com/go-sql-driver/mysql"// これは、自分で追加が必要
    "github.com/jinzhu/gorm"
)
// ないとき
//go get github.com/jinzhu/gorm
//go get github.com/go-sql-driver/mysql

まず、データベースと接続をします

main.go
func openGormDB() *gorm.DB { // localhost
    DBMS := "mysql"
    USER := "user"//mysqlのユーザー名
    PASS := "password"//パスワード
    PROTOCOL := "tcp(localhost:3306)"
    DBNAME := "sample"//データベース名

    CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=True&loc=Local"
    db, err := gorm.Open(DBMS, CONNECT)
    if err != nil {
        panic(err)
    }
    return db
}

これは、データベースの内容

main.go
type ShiromiyaChannelInfos struct {
    //ID              uint64
    ChannelID       string
    ChannelName     string
    ViewCount       uint `gorm:"type:int"`
    SubscriberCount uint `gorm:"type:int"`
    VideoCount      uint `gorm:"type:int"`
    CreatedAt       time.Time
}

データベースの内容を取る関数
テーブルごとに書かく

データベースの内容を取る関数
// この関数みたいな関数を何度も書かないといけない
func GetDBShiro() []ShiromiyaChannelInfos/*<=①*/ {
    db := openGormDB()
    var shiroInfo []ShiromiyaChannelInfos/*<=①*/
    db.Find(&shiroInfo)/*<=①*/
    db.Close()
    return shiroInfo/*<=①*/

func GetDBHashi() []HashibaChannelInfos/*<=①*/ {
    db := openGormDB()
    var hashiInfo []HashibaChannelInfos/*<=①*/
    db.Find(&hashiInfo)/*<=①*/
    defer db.Close()
    return hashiInfo/*<=①*/
}

データベースに接続して目的のテーブルの内容を全て取得する関数
上の①と、したところだけしが違う  
あとは、ほとんど同じ  

上の(データベースの内容を取る関数)ようなものを何度も書かないといけないのでまとめた

main.go
// これは、完成したものです
func AllGetDBChannelInfo(chInfo string) (interface{}, error) {
    db := openGormDB()
    defer db.Close()

    switch chInfo {
    case "ShiromiyaChannelInfos":
        var channelInfo []entity.ShiromiyaChannelInfos
        db.Find(&channelInfo)
        return channelInfo, nil
    case "HashibaChannelInfos":
        var channelInfo []entity.HashibaChannelInfos
        db.Find(&channelInfo)
        return channelInfo, nil
    case "ChannelInfos":
        var videoInfo []entity.VideoInfos
        db.Find(&videoInfo)
        return videoInfo, nil
    default:
        return nil, errors.New("そのdb_nameありません")
    }
}

問題点:
AllGetDBはあとで関数を追加するときAllGetDBを上書きしなければならない

GetDBは重複したものが多くなる関数も多くなる。でも、関数を上書きしなくていい追加するだけ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む