gormを使ってみた

### はじめに
WebカメラのサイトのサムネイルのURLを格納しているテーブルを参照し、それぞれの画像をダウンロードして保存するプログラムを作成した。テーブルの参照、更新はgormを使用したが、思ったより使うのがしんどかった。

### 環境
* go 1.17 → go 1.20
* Windows 10
* PostgreSQL 14

### golangのバージョンアップ
プログラムを作る以前にgormのインポートからうまくいかず。
gorm.io/driver/postgresをインポートしようとして、go getしようとしたが、エラー。1.17から1.20にアップデートして解消した。
```batch
go get gorm.io/driver/postgres  
C:\Users\xxxx\go\pkg\mod\github.com\jackc\pgx\v5@v5.3.1\pgtype\builtin_wrappers.go:9:2: package net/netip is not in GOROOT 
(C:\Program Files\Go\src\net\netip)
```
問題なく作業できるかと思ったら、VS Codeでデバッガ起動したときにエラーが出力されて動かず。
```
Failed to launch:Version of Delve is too old for Go version 1.20.5(maximum supported version 1.18 suppress 
this error with --check-go-version=false)
```
なので、delveをアップデートした。
```batch
go get -u github.com/go-delve/delve/cmd/dlv
```
### プログラムの作成
使用したテーブル名は、thumbnail_linkという名前で、以下のような形式。今思えば、更新するなら、IDくらいつけとけばよかったように思う。
| 列名              | データ型       |
|:------------------|:--------------|
|　link             | varchar(100)  |
| download_flag     | integer       | 

テーブル自体は<a href="https://qiita.com/tthogho1/items/08117fe73dbbfc1a74a6">以前作ったもの</a>で、もとのデータは、Webカメラのサイトからとってきたもの。

linkのデータは、次のようなthumbnailのURLが設定されている。
 https://images-webcams.windy.com/92/1177842892/current/thumbnail/1177842892.jpg 
donload_flagは、未ダウンロード：0 ダウンロード済：1で、ダウンロードした後1に更新する。

テーブルに合わせてモデルを定義した。一文字目を大文字にしないと検索しても値が入ってこない。
```golang
type thumbnail_link struct {
	Link          string
	Download_flag int
}
```
モデルをthumbnail_linkに定義した場合、生成されるSQLのテーブル名は、デフォルトでは複数形のthumbnail_linksになってしまうため、テーブル名をカスタマイズする関数を定義した。TableName()Gorm ライブラリで使用されるメソッド。
```golang
func (o thumbnail_link) TableName() string {
	return "thumbnail_link"
}
```
後から知ったが、db.Table("<テーブル名>")でもテーブルを指定することができるとのこと。

https://qiita.com/cobot00/items/02fd9adddf851e72587a

download_flagが0のもののみ取得して、ループしてダウンロードを実行した。
```golang
	var link []thumbnail_link
	db.Where("download_flag = ?", "0").Find(&link)
	for _, v := range link {
		downloadImage(v, db)
	}
```
画像は、http.Getで取得し、取得できたものに対してdownload_flagに1を設定した。
```go
func downloadImage(v thumbnail_link, db *gorm.DB) {
	fmt.Println(v.Link)
	resp, err := http.Get(v.Link)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
    
    <保存処理>  
  
    db.Model(&v).Where("link = ?", v.Link).Update("download_flag", 1)
```
【コード全文】
```golang
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type thumbnail_link struct {
	Link          string
	Download_flag int
}

func (o thumbnail_link) TableName() string {
	return "thumbnail_link"
}

func downloadImage(v thumbnail_link, db *gorm.DB) {
	fmt.Println(v.Link)
	resp, err := http.Get(v.Link)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	lastSlashIndex := strings.LastIndex(v.Link, "/")
	if lastSlashIndex == -1 {
		fmt.Println("スラッシュが見つからなかった")
		return
	}
	// スラッシュの後ろの文字列を取得
	fileName := v.Link[lastSlashIndex+1:]
	savefile, _ := os.Create("c:/temp/" + fileName)
	defer savefile.Close()

	image, _ := ioutil.ReadAll(resp.Body)
	data := []byte(image)
	savefile.Write(data) // 画像書き込み

	db.Model(&v).Where("link = ?", v.Link).Update("download_flag", 1)
}

func main() {
	dsn := "host=localhost user=user1 password=pass dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Tokyo"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	var link []thumbnail_link
	db.Where("download_flag = ?", "0").Find(&link)
	for _, v := range link {
		downloadImage(v, db)
	}
}
```