- 投稿日:2021-02-21T22:56:15+09:00
Sqlboilerの設定ファイルがうまく読み込まれない
Sqlboilerの設定が正しいはずなのに読み込まれない。。
Go言語でSqlboilerを採用したのですが、設定周りで結構ハマってしまってしまったので、備忘録として記載します。
TOMLの設定正しいのに、なぜか読んでくれない。このエラー出る。。って人は参考にしてください。found key user in config, but it was not a string (<nil>)SQLBoiler?
Go言語のORMです。
GORMと、SQLBoilerが大体有名どころみたいです。
GORMは、モデルファーストなフレームワークで評判も良さそうですが、すでにテーブル定義があったので、スキーマファーストらしいSQLBoilerをORMに使うことにしました。使ってみる
sqlboiler.toml(yamlでもいいらしい)をルートに配置して、DBの接続設定を書くと、DBに接続してスキーマからモデルを生成してくれるツールです。
やりかたは公式のReadmeに詳しく載っています。いざ、tomlを書いて、mysqlに接続してみます。tomlはこんな感じ。mysqlのテーブルに接続設定を書けば良いと。
TOMLの構文はこちらが詳しいです。output = "repository/models" pkgname = "repository" #delete output directory if exists. wipe = "true" debug = "true" [mysql] dbname = "db-name" host = "127.0.0.1" port = 3306 user = "user" pass = "pass" sslmode = "false"以下コマンドで生成!
sqlboiler mysqlすると。。。
found key user in config, but it was not a string (<nil>)ええ、、
なんでやねん。-dをつけて、デバック実行とやらをやってみると、
{ "config": { "driver_name": "mysql", "driver_config": { "blacklist": null, "dbname": null, "host": null, "pass": null, "port": null, "sslmode": null, "user": null, "whitelist": null }, "pkg_name": "repository", "out_folder": "models", "debug": true, "wipe": true, "struct_tag_casing": "snake", "relation_tag": "-", "imports": { "all": { "Standard": [ "\"database/sql\"", "\"fmt\"", "\"reflect\"", "\"strings\"", "\"sync\"", "\"time\"" ], "ThirdParty": [ "\"github.com/friendsofgo/errors\"", "\"github.com/volatiletech/sqlboiler/v4/boil\"", "\"github.com/volatiletech/sqlboiler/v4/queries\"", "\"github.com/volatiletech/sqlboiler/v4/queries/qm\"", "\"github.com/volatiletech/sqlboiler/v4/queries/qmhelper\"", "\"github.com/volatiletech/strmangle\"" ] }, "test": { "Standard": [ "\"bytes\"", "\"reflect\"", "\"testing\"" ], "ThirdParty": [ "\"github.com/volatiletech/sqlboiler/v4/boil\"", "\"github.com/volatiletech/sqlboiler/v4/queries\"", "\"github.com/volatiletech/randomize\"", "\"github.com/volatiletech/strmangle\"" ] }, "singleton": { "boil_queries": { "Standard": null, "ThirdParty": [ "\"github.com/volatiletech/sqlboiler/v4/drivers\"", "\"github.com/volatiletech/sqlboiler/v4/queries\"", "\"github.com/volatiletech/sqlboiler/v4/queries/qm\"" ] }, "boil_types": { "Standard": [ "\"strconv\"" ], "ThirdParty": [ "\"github.com/friendsofgo/errors\"", "\"github.com/volatiletech/sqlboiler/v4/boil\"", "\"github.com/volatiletech/strmangle\"" ] } }, "test_singleton": { "boil_main_test": { "Standard": [ "\"database/sql\"", "\"flag\"", "\"fmt\"", "\"math/rand\"", "\"os\"", "\"path/filepath\"", "\"strings\"", "\"testing\"", "\"time\"" ], "ThirdParty": [ "\"github.com/spf13/viper\"", "\"github.com/volatiletech/sqlboiler/v4/boil\"" ] }, "boil_queries_test": { "Standard": [ "\"bytes\"", "\"fmt\"", "\"io\"", "\"io/ioutil\"", "\"math/rand\"", "\"regexp\"" ], "ThirdParty": [ "\"github.com/volatiletech/sqlboiler/v4/boil\"" ] }, "boil_suites_test": { "Standard": [ "\"testing\"" ], "ThirdParty": null } } }, "aliases": {}, "version": "4.4.0" }, "driver_config": { "blacklist": null, "dbname": null, "host": null, "pass": null, "port": null, "sslmode": null, "user": null, "whitelist": null }, "schema": "", "dialect": { "lq": 0, "rq": 0, "use_index_placeholders": false, "use_last_insert_id": false, "use_schema": false, "use_default_keyword": false, "use_auto_columns": false, "use_top_clause": false, "use_output_clause": false, "use_case_when_exists_clause": false }, "tables": null, "templates": null }おう、driver_configが全部null。。。
Issuesとか調べてみると
PSQLで設定ファイル読まれません
まったく同じ現象!
この人はPSQLだけど。。
とりあえずお前の環境がおかしい言われてクローズされてます。
環境変数がどうのこうのとあるので、やっぱり変な設定ファイル読みに行っている可能性が高いということで、SQLBoilerのコードをちょっとみてみることに。。結果、環境変数気をつけるべし
コードを追っかけた結果、原因がわかりました。
sqlboilerのmain.go,270行目(記事執筆時点です。)にこんなコードがあります。
これ、psqlとかmysqlとか書いてあるtomlのテーブルの値を読みに行って、コマンドに渡すところなのですが、ここで、環境変数もみています。func allKeys(prefix string) []string { fmt.Printf("Call allKeys:" + prefix) keys := make(map[string]bool) prefix += "." for _, e := range os.Environ() { splits := strings.SplitN(e, "=", 2) key := strings.ReplaceAll(strings.ToLower(splits[0]), "_", ".") if strings.HasPrefix(key, prefix) { keys[strings.ReplaceAll(key, prefix, "")] = true } } for _, key := range viper.AllKeys() { fmt.Fprintln(os.Stderr, "viper key:", key) if strings.HasPrefix(key, prefix) { keys[strings.ReplaceAll(key, prefix, "")] = true } } keySlice := make([]string, 0, len(keys)) for k := range keys { keySlice = append(keySlice, k) } return keySlice }僕は環境変数に、$MYSQLという変数がありまして、その値をどうやら読んでしまって、おかしくなっていたらしい。
とりあえずMYSQLという名前があるとここで引っかかるので、名前を変更して、実行したら、とおりました。。
結構ハマってしまった。SQLBoiler使う時は環境変数名気をつけた方がいいですよという話でした。
殴り書きですみません。
Issueの人もPSQLという環境変数用意しちゃっていたみたいですね。
- 投稿日:2021-02-21T22:45:38+09:00
go testで"go: cannot find main module; see 'go help modules'"が出てきたとき
Goのバージョンをアップデートした後にgo testを実行したら,
go: cannot find main module; see 'go help modules'と出てきて,先に進まない
調べてみると,GO111MODULEという環境変数が問題らしい
この環境変数が'on'になっているのが問題だと書いてあるところが多いのだが,私の場合は何も入っていなかった対処方法
GO111MODULEを'auto'と設定するとうまくいった
go1.13以降だと,go env コマンドに -w オプションを付けて環境変数を設定できる
go env -w GO111MODULE=autook.
参考文献
- 投稿日:2021-02-21T17:39:16+09:00
GO言語から初めてみました(^O^;)
とにかく右も左もわからず、がむしゃらに本を見ながら打ち込んでみます。プログラミング超初級の方の為の内容です。そして↓のコードはGO言語になります。
hello.gopackage main import "fmt" func main() { fmt.Println("はじめまして!") }package(パッケージ)の中にmain(メイン)という関数?があるってことでいいのかな?
import(インポート)はプログラミングでライブラリ?を読み込むことを示すらしい。で、(””)このダブルクォーテーション内にあるfmt(エフエムティー)というのもパッケージの1つでフォーマット(書式)を思わせるよう、ターミナル?に内容を表示する為の関数?を定義しているようです。
func(ファンク)は関数?を示す語で、横にmain(メイン)を付けるのが決まりだそうです。
そしてfmtパッケージにあるPrintln(プリントライン)という関数?で(はじめまして!)の括弧内の内容を出力させます。
<関数>
とは、色々な処理をまとめた物でGO言語では1つの「データ型」になります。1度入力した物を皆でシェアしたり、他の場面で使ったり、2度書く必要が無い優れものです。関数を作ることを(関数を定義する)といいます。<ターミナル>
とは、コンピュータに文字で指示を行う(コマンド)為のプログラムで、「端末」とも呼ばれます。macOSやLinuxなどのOSで利用され、Windowsでは「コマンドプロンプト」になります。<ライブラリ>
とは、プログラム?の部品を集めたファイルのこと。<プラグラム>
とは、コンピュータに対する命令(処理)を記述したもの。
- 投稿日:2021-02-21T17:11:14+09:00
AtCoder Beginner Contest 192 C - Kaprekar Number(Golang)
はじめに
SOMPO HD プログラミングコンテスト2021(AtCoder Beginner Contest 192)の[C - Kaprekar Number]のメモです。
どのような問題だったか
- 入力は、「N, K」(例:436, 2)
- Nの各桁の数字を大きい順に並べる(例:436が与えられた場合、643にする)
- Nの各桁の数字を小さい順に並べる(例:436が与えられた場合、346にする)
- それぞれ並び替えたものを除算した結果を再度並び替えて除算とK回数繰り返した時の数を出力する(1回目643-346=297, 2回目972-279=693。出力は、「693」)
詳細は、C - Kaprekar Numberにてご確認ください。
主に求められるポイントは以下で、ほぼ言語仕様への理解が必要な問題です。
- 入力された数値を各桁ごとに分解できること
- 数字の降順ソートができること
- 数字の昇順ソートができること
- ソート結果は最終的に数値であること(計算ができること)
解答
2つ提出しました。
- 提出コード①「入力された数値を各桁ごとに分解してソート」
- 提出コード②「入力された数値を文字列に変換し、各桁ごとに分解してソートした後数値に戻す」
愚直に考えると①ですが、数学的知識(3桁の数字から各桁の数値を取り出す)が必要です。
②は文字列にすることで、stringsが使えるためint型より扱いが容易になります。コードも②の方がすっきりしました。
以下は、公式解説のコメントより問題文に指示された通りの3つの関数を作成し、数列を順に計算していけばよいです。
g1及びg2の計算は多くの言語では「数値を文字列に変換してからソートし、数値に戻す」とすると容易です。提出コード①
入力された数値を各桁ごとに分解してソートします。
package main import ( "fmt" "sort" "strconv" ) func main() { var n, k int fmt.Scan(&n, &k) a := n // k回f(x)を繰り返した残り for i:=0; i < k; i++ { a = f(a) } fmt.Println(a) } // 数値→Slice func intToSlice(n int) []int { n_sli := []int{} // 10で割ったあまり=1の位(723%10は3) for n > 0 { n_sli = append(n_sli, n%10) n /= 10 } return n_sli } // Slice→数値 func sliceToInt(ns []int) int { n_str := "" for _, v := range ns { n_str += strconv.Itoa(v) } n_int, _ := strconv.Atoi(n_str) return n_int } // 降順 func g1(x int) int { xx := intToSlice(x) sort.Sort(sort.Reverse(sort.IntSlice(xx))) return sliceToInt(xx) } // 昇順 func g2(x int) int { xx := intToSlice(x) sort.Ints(xx) return sliceToInt(xx) } func f(x int) int { return g1(x) - g2(x) }コード長:923 Byte
実行時間:188 ms
メモリ:6604 KB所感
ソートの書き方が複雑すぎる
①②共にですが、降順ソートの書き方の難解さに圧倒されます。
sort.Sort(sort.Reverse(sort.IntSlice(xx)))文字列と違い、容易に分割することができない
自前で数値→Slice(intToSlice)を用意しています。
ソートするためには、まずXX桁の数値をXX個の要素にする必要がありますので、Sortが使えるスライスに変化します。
コメントに記載していますが、以下の方法により1桁ごとの値を取得できます。入力例)732 1の位、732÷10=73あまり2 →あまりをスライス[0]に格納 先の計算の解を使って10の位、73÷10=7あまり3 →あまりをスライス[1]に格納 先の計算の解を使って100の位、7÷10=0あまり7 →あまりをスライス[2]に格納 先の計算の解が0になったら終了 出力、int[2, 3, 7]結合も容易にできない
自前でSlice→数値(sliceToInt)を用意しています。
最終的に除算するので、ソートされたスライスをint型にする必要がありますが、
単純に要素を足していくと数値型は、2+3+7=12となってしまいます。
そのため、一度文字列にして"237"を作成した後、文字列→数値に変換しています。提出コード②
入力された数値を文字列に変換し、各桁ごとに分解してソートした後数値に戻す
なお、このやり方は一般的ですが、文字列での比較となるので、[2,11,8]など桁数が異なるものが与えられている場合、昇順[2,8,11]としたいところが昇順[11,2,8]となってしまうので使えません。package main import ( "fmt" "sort" "strconv" "strings" ) func main() { var n, k int fmt.Scan(&n, &k) a := n // k回f(x)を繰り返した残り for i:=0; i < k; i++ { a = f(a) } fmt.Println(a) } // 降順 func g1(x int) int { n_sli := strings.Split(strconv.Itoa(x), "") // 文字列→Slice sort.Sort(sort.Reverse(sort.StringSlice(n_sli))) n_int, _ := strconv.Atoi(strings.Join(n_sli, "")) // Slice→数値(文字列) return n_int } // 昇順 func g2(x int) int { n_sli := strings.Split(strconv.Itoa(x), "") // 文字列→Slice sort.Strings(n_sli) n_int, _ := strconv.Atoi(strings.Join(n_sli, "")) // Slice→数値(文字列) return n_int } func f(x int) int { return g1(x) - g2(x) }コード長:786 Byte
実行時間:167 ms
メモリ:6592 KB所感
ソートの書き方は数値型か文字型かでメソッド名は異なるが、基本変わらず
昇順ソートsort.Ints([]int) //int型スライス sort.Strings([]string) //string型スライス文字列→スライス(string型→[]string型)が容易
②から抜粋n_sli := strings.Split(strconv.Itoa(x), "")
strconv.Itoa(int)で、入力数値→文字列の変換をしています。
変換したらstrings.Split(string, "")が使えるので、区切り文字なし(1桁ごと)でスライスできます。
※数値にこのようなメソッドはないようです。スライス→文字列([]string型→string型)が容易
②から抜粋n_int, _ := strconv.Atoi(strings.Join(n_sli, ""))
strings.Join([]string, "")を使うと、スライスの要素を区切り文字なし(1桁ごと)で文字列にできます。
※Split同様、数値にこのようなメソッドはないようです。
最後に計算のため、strconv.Atoi(string)で、文字列→数値の変換をしています。おわりに
並び順をどうにかする問題、毎回ぐるぐる考えてしまうのでどうにかしたいです。
追記
コメントでいただいた[]stringではなく[]byteでソートするやり方が一番きれいではやい!
- 投稿日:2021-02-21T17:07:11+09:00
slackの操作をシェルプロっぽくしてChatOps捗りツール作りました!
まえせつ
slack使ってるとネイティブアプリがWebアプリぽい操作感なのに、ターミナルがシェルなのでモヤる時ありませんか?
- メッセージを再編集するのに↑キーで履歴を選びたい。ネイティブアプリの実装は何か違う感が・・
- 特定の人へのメンションを多用するので毎度コピペするのが面倒。@漏れで読んでくれない、とか。
- 同じ入力をするのにエイリアスみたいなので入力を減らしたい。ファイルサーバーの途中までのパスとか。
特にslack→サーバーへコマンド投げ込みボットが居る場合はとっても面倒になります。
ボットを呼んでコマンド投げて、メンション入れて確認依頼して・・次のコマンド入れて・・をコピペして繰り返すことになります。というのをUXを解消してくれるのが、このツール!
作ったわ!
詳しくはリポジトリで。バイナリをダウンロードして即使いたいならこっち
zlib1.dllが無い場合のエラー
使用しているrobotgoが依存しているのでzlib1.dllが無い場合、エラー落ちが予測されます。
こちらのリンクからダウンロードしてパスが通ってる場所に配置すると動かせますつかいかたは?
実行ファイルを起動すればプロンプトが出ます。そこに入力した内容をslackのネイティブアプリに投げ込んでくれます。
一行モードにすれば書き込みを待たずに一行単位で投げ込んでくれるのでサクサクChatOps!!
あとがき
三か月ぶりの個人開発!!
・・・三か月でさび付いてくるもんなんですね。過去に自分書いたの読めないわ、書けないわ。w
ただ、また思い付きで何か作ってみようってモチベが出てくるのは良い事だわ。続いて、チャットボット側も作っていきたいな。
チームからChatから依頼受けたら、わざわざターミナル上げて、ログインしてログ見て、コピペしてみたいな繰り返しをやめて
チャットからコマンド流して共有して、オペレーション履歴も残って・・みたいな世界観でSREっていきたい!!
- 投稿日:2021-02-21T17:07:01+09:00
Qiita/Zennの投稿をGitHubプロフィールに自動反映するためのツールを作った
この記事は Feed で GitHub の README.md に反映されるはず。という実験用のエントリです。
記事はこちら:https://zenn.dev/ikawaha/articles/20210221-c8f2d9ac028ae49d551a
- 投稿日:2021-02-21T16:52:15+09:00
go の chan のことをよく知らなかったので試してみた
go の chan のことをよく知らなかったので試してみた
知っておくといいかもしれないこと。
go の chan が close済みかどうかを知る方法は、昔あったらしいけど今はない。
途中のバージョンで削除されたらしい。削除された理由はおそらく、無いほうがいいから。あるとむしろトラブルの原因になる。
「close済みでなければ xxx する」というコードを書いても、「close済みでなければ」と「xxxする」の間で他人が close する可能性があり、それを排除することができない。chan が nil だったり close済みだったりする場合の動作
nil の chan からの読み出しと書き込み。
そこで止まり、書き込み・読み出しが終わることはない。
それが唯一の goルーチンの場合、panic する。Recover できない。close済み の chan への書き込み
panic する。Recover できる。
send on closed channelというメッセージ。
型はruntime.plainError。close済み の chan からの読み出し
panic せず、読み取れる。
読むべき値がない場合
ch := make(chan [3]int) close(ch) log.Println(<-ch, "ok") //=> [0 0 0] ok log.Println(<-ch, "ok") //=> [0 0 0] ok log.Println(<-ch, "ok") //=> [0 0 0] okゼロ値が取れる。
close 前に値を積んである場合
ch := make(chan int, 2) ch <- 111 ch <- 222 close(ch) log.Println(<-ch, "ok") //=> 111 ok log.Println(<-ch, "ok") //=> 222 ok log.Println(<-ch, "ok") //=> 0 ok log.Println(<-ch, "ok") //=> 0 okclose 前に積んだ値が読める。
積んだ値がなくなったらゼロ値になる。nil の chan を含む select
nil である chan から読み出しを試みるとそこで止まってしまうが、 select の場合は止まらずに「読めなかったね、じゃあ次」となるので問題ない。
chI := make(chan int) var chF chan float64 = nil go func() { time.Sleep(500 * time.Millisecond) chI <- 123 }() select { case i := <-chI: log.Println("i:", i) // ここに来る。 case f := <-chF: log.Println("f", f) // ここには来ない。 }上記の例では、500ms 後に
chIから123が読み出され、特に困ったことは何も起きない。close 済み chan を含む select
close 済みの chan から読み込むとゼロ値が取れるので、以下の例では即座に close 済みの
chFから0.0が読まれる。
500ms 後にchIに積まれる 123 は、このコードでは読まれない。chI := make(chan int) chF := make(chan float64) close(chF) go func() { time.Sleep(500 * time.Millisecond) chI <- 123 }() select { case i := <-chI: log.Println("i:", i) // ここには来ない。※ case f := <-chF: log.Println("f", f) // ここに来る。 }※ select を含む go ルーチンが何らかの事情で goルーチン作成から
select到達までに 500ms 以上の時間を要していたらchIの方に来るかもしれない。nil である chan の range
nil である chan を range にわたすと、そこで処理が止まる。
var ch chan int = nil for i := range ch { log.Println(i) } log.Println("exit for")ループ内に入ることもないし、ループを抜けることもない。
このループが唯一の go ルーチンである場合、recover 不能な panic が発生する。close 済み chan の range
当たり前だけど、
for..rangeのループが始まってから close したのと同じような状況になる。ch := make(chan int) close(ch) for i := range ch { log.Println(i) // ここには来ない } log.Println("exit for") // すぐにここに来るつまり、ループには入らず、すぐにループを抜ける。
nil である chan と close 済み chan の close
両方とも panic になる。
型はruntime.plainError。
メッセージは「close of nil channel」と「close of closed channel」。まとめ
やること nilである chanclose 済み chan 読み出し そこで停止 ゼロ値が得られる 書き込み そこで停止 パニック select〜caseその case にはならない ゼロ値が得られる rangeそこで停止 ループに内入らずにループ終了 close panic panic こうしてみると、
nilである chan は、ほぼ、書き込み側からは「誰も読んでくれない バッファのない chan」、読み出し側からは「誰も書いてくれない chan」として機能しているが、 close の振る舞いだけが異なる。chan の nil チェックを含むパターン
フィールドの nil チェック後に read / write
こんなの。
type hoge struct { ch chan struct{} } func (h *hoge) foo() { if h.ch == nil { return } // [A] a := <-h.ch // [B] doSomething(a) }これはダメ。 [A] の箇所で誰かが
h.chをnilにするかもしれない。nilになると [B] で止まる。
上記の例は read だけど、write も同じ理由でダメ。ローカル変数の nil チェック後に read
こんなの。
func (h *hoge) bar() { ch := h.ch if ch == nil { return } // [A] a := <-ch // [B] doSomething(a) }これは、明らかに駄目ということではないが、わりとよろしくない感じ。
[A] でh.chを誰かが nil にしても、chにはその前の値が入っている。
ただ
chはすでに close 済みで、[B] で読める値はゼロ値かもしれない。ということで、わりとまずいことになりそうではある。
ローカル変数の nil チェック後に write
こんなの。
func (h *hoge) baz() { ch := h.ch if ch == nil { return } ch <- struct{}{} // [B] }[B] が
nilへの write にならないことは間違いないが、chが close 済みだとパニックになる。つまり、事前条件と結果は
h.chが close 済みではない →chになんか送るh.chが close 済み →panich.chがnil→ なにもしないということになる。
read の例と同じく、[B] の時点ではh.chはnilかもしれないので、chを read する人はもういないかもしれない。というわけで
というわけで、 chan が
nilかどうかで動作を変えるのはわりとうまく行かない感じ。
「close したら nil にして、nil かどうかで条件分岐」とかいう作戦より「そもそも close かどうかが不確定な状況にならないようにする」という作戦を取るべきと思う。read 側が range で回せば write 側の close でループ抜けるので、それで済むならそれがよい。
あるいは、一回しか write しなくて、書き込み側は write → close。読み出し側は read 一回とか。chan の仕様がそのようなメッセージとなっていると思う。
複雑なケースの場合に安全なパターンを作るのが難しくなることもあると思うけど、 nil チェックでは安全にしにくい。
あと。close 済みの chan を read するとゼロ値が得られるのが気持ち悪いような便利なような。
たとえば、chan boolにしておいて、いつもch<-trueとしておけば、falseが得られた場合に close 済みだということを知ることができる。
- 投稿日:2021-02-21T16:31:42+09:00
バッファって何?
はじめに
GoのChannelに関して学んでいる最中に,バッファという単語が出てきました.
聞いたことはあるけど,理解はできていない?
いい機会なので,調べてみました!簡単に
データをやりとりするときの一時的な記憶領域
のこと.きちんと
英語ではBufferと書いて,「緩衝材」のような意味を持つようです.
コンピュータの「一時的な記憶領域」のことを指していて,それが転じてビジネスでは「ゆとり」などの意味合いで使われるようになったらしです?GoのChannelはデータを保持するためのデータ構造で,容量を指定できます.
これがバッファです.
cap関数を使うことで,見ることができます.main.gochannel := make(chan int, 5) fmt.Println(cap(channel))terminal0と出力されました.
このmake関数で,第2引数に指定された 5 がバッファサイズです.
今は何も受信していないため,容量は 0 と表示されました.ではこの後に,以下のように送信すると,
main.gochannel <- 1 channel <- 2 channel <- 3 channel <- 4 channel <- 5 channel <- 6 fmt.Println(cap(channel))terminalfatal error: all goroutines are asleep - deadlock!と出力されました.
この deadlock は保存領域が足りないよと言うことで,
バッファのサイズが足りないと怒られてしまったのですね?Channelはqueueの性質を持っていで,バッファはqueueのサイズともいえます.
queueは先入れ先出しの性質を持っているので,errorを解消するには,受信をしてしまえばいいのではないでしょうか.
main.gochannel <- 1 channel <- 2 channel <- 3 channel <- 4 fmt.Println(channel<-) channel <- 5 channel <- 6 fmt.Println(cap(channel))terminal1 5こんな感じで出力されました!
この1は先入された1ですね?
5は保存されている容量だと思います.まとめ
自分の手で動かしてみて,バッファは
データをやりとりするときの一時的な記憶領域
と言うことが理解できたような気がします?♂️
- 投稿日:2021-02-21T01:30:58+09:00
[Golang] 構造体埋め込み時のバリデーションについて
GolangのORMライブラリ
GORMで、モデルの関連づけを表現した際のバリデーションにつまづいたのでメモ。使用ライブラリ群
困ったこと
GORMで以下のようなHasOne関係のモデルを作り、それぞれの構造体に
validateタグを付与している状況。type User struct { ID int `json:"id" gorm:"primary_key"` Email string `json:"email" validate:"email,required,max=255"` Password string `json:"password" validate:"required,min=8,max=16"` Profile Profile `json:"profile" gorm:"foreignKey:ID"` } type Profile struct { ID int `json:"id" gorm:"primary_key"` UserID int `json:"userId" validate:"required"` Name string `json:"name" validate:"required,max=255"` Hobby string `json:"hobby"` }UserのみのCreate(Profileは作らない。Save前にValidation実行)をするAPIを用意し、以下のパラメーターをPOSTすると、
{ "email": "hoge@exa.com", "password": "hogehoge" }以下のようなバリデーションエラーとなる。
Key: 'User.Profile.UserID' Error:Field validation for 'UserID' failed on the 'required' tag Key: 'User.Profile.Name' Error:Field validation for 'Name' failed on the 'required' tag原因は単純で、Userに埋め込んでるProfileの
validateタグも見にいってるので、バリデーションエラーとなる。困った。解決策
validatorのリファレンスを探ってると、Skip Fieldなるものを発見。
validate:"-"で検証をスキップできるみたい。User.Profileに追記する。type User struct { ID int `json:"id" gorm:"primary_key"` Email string `json:"email" validate:"email,required,max=255"` Password string `json:"password" validate:"required,min=8,max=16"` Profile Profile `json:"profile" gorm:"foreignKey:ID" validate:"-"` // ココに追記した } ...Profileの検証がスキップされてUserの作成に成功。
気付き
- jsonタグでも
"-"(ハイフン)でフィールドが無視できたと思うので、struct tagsのvalueにおける"-"は無視する的な使い方をするっぽい?


