質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.60%

  • Go

    482questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

gormとgo-sqlite3の併用の仕方を教えてください!

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 635

DDxlk

score 115

まず

echoに関する質問3回目です。
いつもお世話になってます。

やったこと

gormを少し触りました。
当方DBを扱ったことがないのでなんとなくですが。
gormのドキュメントを真似してファイルを作ることはできました。
以下該当コード
DB関係は別パッケージに切り出しました。
まずmain.goで関数を呼び出す部分

e.GET("/api/register", func(c echo.Context) error { //アクセスするだけでいいため、とりあえずGETにしています
        plugin.CreateDB() //下記で示す関数です
        return c.Render(http.StatusOK, "index", nil) //特に何も返したくない場合何を書けばいいのかわからなかったので、適当に書きました
    })


CreateDB関数(このような関数は実際には必要ないかと思われますが、テストなのでファイルを作るだけの関数を用意しました)

type Product struct { //おそらくDBの形式を模した何か(?)
    gorm.Model
    Code string
    Price uint
}

func CreateDB() {
    db, err := gorm.Open("sqlite3", "test.db") //ファイル作成
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    db.Create(&Product{Code: "L1212", Price: 1000}) //先ほどの構造体を利用して書き込み
}

このような感じです。

質問

  • go-sqlite3はどのようにgormと併用すればいいのか
  • ドキュメントでは何故か_ "github.com/jinzhu/gorm/dialects/sqlite"がインポートされていたがそれはなんなのか

が知りたいです。

ログインのサブミットに関して

サブミットの処理はhtmlで"/api/login"にpostするようにして、go側のe.POST()の中にDB関係の処理を書けばいいのかなと思っています。
なのでDBに関してを勉強しているというわけです。

追記

・GORMのCreateについて
そもそも、GORMのCreateというのは、データベースのテーブルを作るという意味でしょうか?
つまり、

db.Create(&Product{Code: "L1212", Price: 2000})
db.Create(&Product{Code: "L1213", Price: 3000})
db.Create(&Product{Code: "L1214", Price: 4000})
db.Create(&Product{Code: "L1215", Price: 5000})
db.Create(&Product{Code: "L1216", Price: 6000})
db.Create(&Product{Code: "L1217", Price: 7000})

このようなコードを実行すると、

 Code    Price 
 L1212   2000 
 L1213   3000 
 L1214   4000 
 L1215   5000 
 L1216   6000 
 L1217   7000 

こういったテーブルを作成するということでしょうか。

・マッピングとは
マッピングとは噛み砕くとどういう処理でしょうか?
ORM(オブジェクト関係マッピング)特有の用語なのかなというのはわかるのですが。。

db.AutoMigrate(&Product{})


このコードはどういう場合(タイミング)に書くものなんでしょうか?

・Readについて

var products []Product
// Get all records
db.Find(&products)
//// SELECT * FROM products;


これはProductテーブル内の全てのレコードを取得するという意味かと思うのですが
セレクトしたものをプリントしようとして

aa := db.Find(&products)
fmt.Println(aa)


こう書いたのですが、

&{0xc420179280 <nil> 6 0xc420174dc0 false 0 {0xc4200fbf40} 0xc420418420 map[] 0xc42016f710 <nil> <nil> false}


このように出力されました。

これは、SELECTによって取得したものは、DBの操作でしか扱えない(型の問題上)ということなんでしょうか。

・go-sqlite3パッケージについて
mattnさんは回答において、go-sqlite3パッケージの関数等を直接使ってらっしゃらないと思うのですが、go-sqlite3はどこで使うのですか?
それとも、

//db, err := gorm.Open("ドライバ名称", "接続文字列")
db, err := gorm.Open("sqlite3", "foo.sqlite")


go-sqlite3はドライバという役割であって、GORMと併用する場合は直接使わないものなんですかね?

質問攻めすみません。

追記2

とりあえず、gormを使ってDBにレコードを作ることはできるようになりました。
あとややこしく考えていましたが、GORMは、単純にGo言語でsqlを楽に扱うためのライブラリってことに気がついて気が楽になりました。

レコード作成はこうですね!↓(質問ではないので軽く見流していただければ結構です 汗)

type UserData struct {
    gorm.Model //ここにCreatedAtがあるのでタイムスタンプは任せればいいですね。
    Userid string
    Password string
}

func main() {
    saveUserData("DDxlk", "DDxlkDayo")
}

func SaveUserData(userid, password string)  {
    db, err := gorm.Open("sqlite3", "test.sqlite3")
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    // Migrate the schema
    db.AutoMigrate(&UserData{})
    // Create
    user := &UserData{Userid: userid, Password: password}
    db.Create(&user)
}

質問1

useridとpasswordをhtmlのフォームからechoに送ってdbに登録するわけですが、dbに何も書き込まれません(レコードのuseridとpasswordの部分が空白になってしまいます)
registration.html(登録)のフォームの部分が以下です

<form action="/api/register" method="post" name="registration_form">
    <input type="text" name="userid" id="userid_form"/>
    <input type="password" name="password" id="password_form"/>
    <input type="hidden" name="register" value="register">
    <a id="login_button" onclick="document.registration_form.submit();return false;">Register</a>
  </form>


そして、打ち込まれたuseridとpasswordをdbに登録する処理が以下です

//echo
e.POST("/api/register", func(c echo.Context) error {
    id := c.Param("userid")  //フォームからidを取得しているつもり
    password := c.Param("password")  //フォームからpasswordを取得しているつもり
    SaveIdOnDB(id, password)
    return c.Render(http.StatusOK, "index", nil)
)}

type UserData struct {
    gorm.Model 
    Userid string
    Password string
}

func SaveUserData(userid, password string)  {
    db, err := gorm.Open("sqlite3", "test.sqlite3")
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    // Migrate the schema
    db.AutoMigrate(&UserData{})
    // Create
    user := &UserData{Userid: userid, Password: password}
    db.Create(&user)

質問2

先ほどからtest.sqlite3の中にレコードを作成してるわけなんですが、レコードの値をコマンドラインに出力するにはどうすればいいのでしょうか。(先日コメントでも申し上げた通り、fmt.Printlnするとポインタの値が出力されます)

今はそれがわからないため、DB Browser for SQLiteというGUIソフトで中身を閲覧しています。
DB Browser for SQLite

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

 go-sqlite3はどのようにgormと併用すればいいのか

gorm の使い方は

  • 接続: ファイル形式、URI 形式
  • マッピング: テーブルと struct のマッピング
  • 検索: First や Last、Find 等を使った単一行検索、Find を使った複数行検索
  • 操作: NewRecord や Create を使った挿入、Update を使った更新、Delete を使った削除

が標準的な使い方です。

 接続

db, err := gorm.Open("ドライバ名称", "接続文字列")


ドライバ名称、接続文字列は各ドライバで異なります。go-sqlite3 であれば

db, err := gorm.Open("sqlite3", "foo.sqlite")


という db ファイルのパスを指定する事も出来ますし、URI 形式でも接続出来ます。

https://sqlite.org/uri.html

 マッピング

type Product struct { //おそらくDBの形式を模した何か(?)
    gorm.Model
    Code string
    Price uint
}

この形式は、gorm.Model を内包する形式です。こう書く事で実際は以下の struct メンバも内包される事になります。

type Model struct {
  ID        uint `gorm:"primary_key"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time
}

gorm を使われるならば、このままで良いと思います。この CreatedAt や UpdatedAt、DeletedAt は DB 操作である Create や Delete、Update 等を使えば自動で更新されます。また ID も Find 等で利用される為、開発者は ID をそれほど意識しなくても良くなります。

マッピングは AutoMigrate を呼び出すだけです。

db.AutoMigrate(&Product{})

使われるテーブルを全てここでマッピングして下さい。gorm はなかなか優秀で、マッピングだけでなくマイグレートも行ってくれます。開発途中で struct フィールドを増やしてもある程度は gorm が修正してくれます。ただし、あまりに大きい修正は追従してくれませんので、大きな変更をする際には DB ファイルをバックアップしていた方が良いです。

なお、gorm 等の様に ORM を使う場合、Create Table すらも ORM に任せるのが一般的ですが、テーブルのフィールドに gorm: のプレフィックスタグを付ける事で既存の DDL を使ったテーブル操作も出来ます。

 検索

http://jinzhu.me/gorm/crud.html#query

db.First(&product)
//// SELECT * FROM products ORDER BY id LIMIT 1;

db.Last(&product)
//// SELECT * FROM products ORDER BY id DESC LIMIT 1;

db.Find(&products)
//// SELECT * FROM products;

db.First(&product, 10)

検索の基本形式です。product は

var product Product


で宣言した変数を渡し、projects は

var products []Product


で宣言した変数を渡して下さい。

 操作

http://jinzhu.me/gorm/crud.html

このページにおおよそ書かれています。ID に値を入れずに Create を呼び出して下さい。フィールドの値を変更して Update を実行すれば更新、ID 付きの物を Delete すれば削除になります。

 ドキュメントでは何故か_ "github.com/jinzhu/gorm/dialects/sqlite"がインポートされていたがそれはなんなのか

これは、このプログラムが sqlite3 のデータベースを使いますよというおまじないです。golang ではインポートするけど参照しない物は _ で名付けします。なんとなく、github.com/mattn/go-sqlite をインポートすれば良い気がするかもしれませんが、gorm の様な ORM は、各 DBMS の SQL 方言を吸収する必要がありますよね。そこで各ドライバ毎に dialect (方言) を設定するのです。
sqlite には方言がないので処理が書かれていませんが、postgres のドライバは幾らか方言用の処理が書かれています。

https://github.com/jinzhu/gorm/blob/master/dialects/postgres/postgres.go

sqlite3 だけでなく他のデータベースにもつなげたいならば、使う分 import する必要があります。

import _ "github.com/jinzhu/gorm/dialects/mysql"
// import _ "github.com/jinzhu/gorm/dialects/postgres"
// import _ "github.com/jinzhu/gorm/dialects/sqlite"
// import _ "github.com/jinzhu/gorm/dialects/mssql"

追記2について

以下のコードで test.sqlite3 にレコードが追加されている(foo/barも入っている)事は確認出来ています。
DB Browser for SQLite のリロードボタンなどで最新が表示されないでしょうか。

package main

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
)

type UserData struct {
    gorm.Model
    Userid   string
    Password string
}

func main() {
    db, err := gorm.Open("sqlite3", "test.sqlite3")
    if err != nil {
        panic("failed to connect database: " + err.Error())
    }
    defer db.Close()
    // Migrate the schema
    db.AutoMigrate(&UserData{})
    // Create
    user := &UserData{Userid: "foo", Password: "bar"}
    db.Create(&user)
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/11 19:28

    いつも回答ありがとうございます。
    DB、ひいてはgormに関して少〜しずつ理解出来て参りました。
    さらに回答を読ませていただいた上で疑問に感じたことを追記させていただきました。
    よろしくお願いしますm(_ _)m

    キャンセル

  • 2017/09/11 20:08 編集

    > そもそも、GORMのCreateというのは、データベースのテーブルを作るという意味でしょうか?

    いえ、レコードを作る関数です。プライマリキーを指定せずに NewRecord を呼び出すと、新規レコードかどうかを true/false で返すので true の場合に Create、false の場合に Update する事になります。

    なおテーブルを作るのは `db.CreateTable(&Product{})` です。

    > マッピングとは噛み砕くとどういう処理でしょうか?

    テーブルのスキーマと struct と付け合せを行います。形があっているか、あってないのか、あってないなら alter table する、といった事をやっています。DB 接続を得た後、また Create や Find 等を呼ぶ前、に呼び出します。

    >これは、SELECTによって取得したものは、DBの操作でしか扱えない(型の問題上)ということなんでしょうか。

    ここは DB や gorm に関係ない話で、単に fmt.Println が構造体ポインタを表示しているだけです。Product に String() という文字列を返すメソッドを追加すると中身を(自分で)表示する事が出来ます。なお、fmt.Sprintf や fmt.Fprintf/fmt.Printf の書式部に %+v を渡すと中身を展開してくれます。

    https://play.golang.org/p/m7DmtKE6qK

    > go-sqlite3 について

    import _ "github.com/jinzhu/gorm/dialects/sqlite"

    という行を書かれておられると思いますが、このパッケージの中で go-sqlite3 をインポートしてくれています。

    https://github.com/jinzhu/gorm/blob/master/dialects/sqlite/sqlite.go

    なぜ github.com/jinzhu/gorm/dialects/sqlite をインポートする必要があるのかについては↑に書いてある通りです。

    キャンセル

  • 2017/09/11 23:25

    なるほど、ありがとうございます
    おおお、"github.com/jinzhu/gorm/dialects/sqlite"ここの中にgo-sqlite3が組み込まれてたんですね!

    キャンセル

  • 2017/09/11 23:41 編集

    データベースが初めてということで混乱して出力などという変なことしてましたね
    a := db.First(&product)
    でDB型を取得して、a.method()やhoge.method(a)等で進めるんですね。w
    意味不明な質問をしてしまい、すみませんでした

    キャンセル

  • 2017/09/12 00:26

    gorm ではクエリをメソッドチェインで作成できるというのはお気づきの通りです。

    db.Model(&Product{}).Where("foo = ?", 3).Pluck("bar", &bar)

    db.Model(&Product{}).Where({Foo: 3}).Pluck("bar", &bar)

    ただし First や Find は最後に値を取得する関数なので First/Find に続く物は Error など、抽出後のメソッドやフィールドになりますね。

    キャンセル

  • 2017/09/13 20:47

    長らくお待たせしました。
    すこし忙しかったため遅れました
    とりあえずなんとなく把握できました。
    あと二つだけ聞きたいことがあるので、追記させていただこうと思います。
    とりあえずそれでこの質問はmattnさんをBAとして区切りますので、あと少しばかりお付き合いくださいm(_ _)m

    キャンセル

  • 2017/09/14 14:13 編集

    追記ありがとうございます。
    私の追記2にも書かせて頂いてますが、単純なレコードのCreateに関しては出来るんです。
    ただ、echoを経由してhtmlのフォームから読み取る方法が上手くできません。(私のコードではc.Param("nameアトリビュート")を使って取得しようとしている)

    キャンセル

  • 2017/09/14 17:11 編集

    Param は URL パラメータを取る物です。POST データを取る場合は ctx.FormValue(name) を使って下さい。

    キャンセル

  • 2017/09/14 17:46

    うまくできました!
    ありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Go

    482questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。