20200713のGoに関する記事は8件です。

Golangでプログレスバーを表示するためのパッケージ3選

Golangで処理を書いている時、特に時間がかかる処理の場合、進捗がどのくらい進んでいるのかを知りたい時がありますよね。そんな時使うのがプログレスバーだったりするわけですが、いい感じで利用できるパッケージがないか探してみると思いの外見つかったので3つリストアップしてみました!

リストアップするだけだと味気ないのでfor文を回す処理を利用してそれぞれ出力してみました。

マルチプログレスバーについての記事も書いているので興味のある方はこちらも是非!
Golangでマルチプログレスバーを表示したい!!

pb

>>リポジトリ

>>ドキュメント

今回紹介する3つの中でスター数が2.7kと一番多い人気のパッケージです。人気なだけあって柔軟性が高そうな印象を受けました。

以下のコマンドでインストールできます。

terminal
go get github.com/cheggaaa/pb/v3

pbをインストールして以下のサンプルを実行してみました。

main.go
package main

import (
    "time"
    "github.com/cheggaaa/pb/v3"
)

func main() {
    count := 100

    bar := pb.Simple.Start(count)
    bar.SetMaxWidth(80)

    for i := 0; i < count; i++ {
        bar.Increment()
        time.Sleep(time.Millisecond * 30)
    }
    bar.Finish()
}

動かした結果がこれ。

pb.gif

uiprogress

>>リポジトリ

>>ドキュメント

同時に複数の進行状況バーをレンダリングでき、また、新しく処理が追加された場合でも追加の進行状況バーを追加できるようです。

以下のコマンドでインストールできます。

terminal
go get -v github.com/gosuri/uiprogress

uiprogressをインストールして以下のサンプルを使って実行してみました。

main.go
package main

import (
    "time"
    "github.com/gosuri/uiprogress"
)

func main() {
    count := 100
    uiprogress.Start()
    bar := uiprogress.AddBar(count)

    bar.AppendCompleted()
    bar.PrependElapsed()

    for bar.Incr() {
        time.Sleep(time.Millisecond * 20)
    }
}

実行結果がこれ。

uiprogress.gif

progressbar

>>リポジトリ

>>ドキュメント

OSに依存せず利用できる、非常にシンプルなスレッドセーフなプログレスバーと言う謳い文句で案内されています。

以下のコマンドでインストールできます。

terminal
go get -u github.com/schollz/progressbar

progressbarをインストールして以下のサンプルを使って実行してみました。

main.go
package main

import (
    "time"
    "github.com/schollz/progressbar"
)

func main() {
    count := 100
    count64 := int64(count)
    bar := progressbar.Default(count64)
    for i := 0; i < count; i++ {
        bar.Add(1)
        time.Sleep(40 * time.Millisecond)
    }
}

実行するとこんな感じ

progressbar.gif

まとめ

個人的に、使い心地と見た目でpbが好みでした。

これ採用して早速実装してみます!

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

Goでのswitchを用いた条件分岐

背景

Javaで散々苦しんだswitch文、Goにも出てきたのでまとめます

環境

go version go1.14.4
windows10

まとめ

JavaとGoのswitch文の大きな違い

Java

条件に合致した際に次の処理を実施しないようにするにはbreakが必要

Go

条件に合致したら次の処理は実施されない。
実施したい場合はfallthroughを入れる必要がある
※型でのswitchの場合は使用できない

ソースと実行例

main.go
package main

import (
    "fmt"
)

// 型アサーションを用いたスイッチの使用例
func main() {

    // 変数aをすべての型を取れるinterface{}型として定義し、999.999を入れる
    var a interface{} = 999.999

    // aの型でswitchする
    // [変数名].(type)で型が取得できる
    switch a.(type) {
    case bool:
        fmt.Printf("bool")
    case int, uint:
        fmt.Printf("int or uint")
    case string:
        fmt.Printf("string")
    case float64:
        fmt.Printf("float")
    case rune:
        fmt.Printf("rune")
    default:
        fmt.Println("don't know")
    }
}

実行例

C:\go>go run main.go
float

999.999はfloat64の型に入るのでその出力。
後の実装のrune, defaultは実行されない。

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

golangでミニマムなREST APIを作る

この記事は何?

注) golang初学者の私が初学者のために書いています。

https://tutorialedge.net/golang/creating-restful-api-with-golang/ のチュートリアル(英語だったが平易な文章で書かれてる。本当に)の内容をベースにして、challengeにあるupdate機能の追加の問題とMySQLを導入してみたという掛け算的内容です。
なので、上記チュートリアルが終了した段階からスタートするため悪しからず。

チュートリアル中のコードで参考になったこと。
https://qiita.com/ngplus6655/items/a38660313383d3ff2136

環境

ubuntu18.04LTS
go1.12.17 linux/amd64

github.com/gorilla/mux
github.com/jinzhu/gorm

updateArticleを追加

まずはチュートリアル中のchallengeの内容を説いてみた

func updateArticle(w http.ResponseWriter, r *http.Request){
    vars := mux.Vars(r)
    id := vars["id"]
    reqBody, _ := ioutil.ReadAll(r.Body)
    var updateArticle Article
    json.Unmarshal(reqBody, &updateArticle)
    for index, article := range Articles {
        if article.Id == id {
            updateArticle.Id = id
            Articles[index] = updateArticle
        }
    }
}

returnSingleArticleと同様urlのidパラメータから編集するarticleを特定し、直接Articlesスライスの要素に代入しています。
直前にupdateArticle.Id = idとしているのは、パラメータidと送られたjsonデータのidが不一致だとArticleに対してIDがユニークではなくなるため。

マルチプレクサにupdateArticleを登録

myRouter.HandleFunc("/article/{id}", updateArticle).Methods("PUT")

curlしてみる

curl -X PUT  -H "Content-Type: application/json" -d '{"id": "1", "Title": "Updated Post", "desc": "the description for my updated post", "content": "my articles content"}' http://localhost:10000/article/1

curl -X GET http://localhost:10000/all
[{"Id":"1","Title":"Updated Post","desc":"the description for my updated post","content":"my articles content"},
{"Id":"2","Title":"Hello 2","desc":"Article Description","content":"Article Content"},
{"Id":"3","Title":"Hello 3","desc":"Article Description","content":"Article Content"}]

意図したとおりにidが1の要素を更新できました。

mysqlの導入

完成形

main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "encoding/json"
    "io/ioutil"
    "strconv"

    "github.com/gorilla/mux"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type Article struct {
    gorm.Model
    Title string `json:"Title"`
    Desc string `json:"desc"`
    Content string `json:"content"`
}

type Articles []Article

func connectDB() (*gorm.DB, error) {
    db, err := gorm.Open("mysql", "ユーザ名:パスワード@/test?charset=utf8&parseTime=True&loc=Local")
    return db, err
}

func initDb() *gorm.DB {
    db, err := connectDB()
    if err != nil {
        log.Fatalln("データベースの接続に失敗しました。")
    }
    return db
}

func idParamToUint(r *http.Request) uint {
    vars := mux.Vars(r)
    id, _ := strconv.Atoi(vars["id"])
    var uid uint = uint(id)
    return uid
}

func ParseJsonArticle(w http.ResponseWriter ,r *http.Request) Article {
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article
    json.Unmarshal(reqBody, &article)
    json.NewEncoder(w).Encode(article)
    return article
}

func homePage(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homePage")
}

func returnAllArticles(w http.ResponseWriter, r *http.Request){
    fmt.Println("Endpoint Hit: returnAllArticles")
    db := initDb()
    var articles Articles
    db.Find(&articles)
    json.NewEncoder(w).Encode(articles)
}

func returnSingleArticle(w http.ResponseWriter, r *http.Request){
    fmt.Println("called returnSingleArticle")
    uid := idParamToUint(r)
    db := initDb()
    var article Article
    db.Where("id = ?", uid).First(&article)
    json.NewEncoder(w).Encode(article)
}

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    fmt.Println("called createNewArticle")
    db := initDb()
    article := ParseJsonArticle(w, r)
    db.Create(&article)
    if db.NewRecord(article) {
        log.Println("新規articleの保存に失敗しました。")
    }
}

func updateArticle(w http.ResponseWriter, r *http.Request){
    fmt.Println("called updateAtricle")
    uid := idParamToUint(r)
    db := initDb()

    updatedArticle := ParseJsonArticle(w, r)

    var article Article
    db.Where("id = ?", uid).First(&article)

    article.Title = updatedArticle.Title
    article.Desc = updatedArticle.Desc
    article.Content = updatedArticle.Content
    db.Save(&article)
}

func deleteArticle(w http.ResponseWriter, r *http.Request) {
    fmt.Println("called deleteAtricle")
    uid := idParamToUint(r)
    db := initDb()
    db.Delete(Article{}, "id = ?", uid)
}

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    myRouter.HandleFunc("/article/{id}", returnSingleArticle).Methods("GET")
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    myRouter.HandleFunc("/article/{id}", updateArticle).Methods("PUT")
    myRouter.HandleFunc("/article/{id}", deleteArticle).Methods("DELETE")
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}


func main() {
    fmt.Println("Rest API v2.0 - Mux Routers")
    db, err := connectDB()
    if err != nil {
        log.Fatalln("データベースの接続に失敗しました。")
    }
    defer db.Close()
    db.AutoMigrate(&Article{})

    handleRequests()
}

MySQLのインストール

$ sudo apt install mysql-server mysql-client
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.30, for Linux (x86_64) using  EditLine wrapper

起動

$ sudo service mysql start

初期設定->データベース作成->ユーザ追加

参考 https://qiita.com/houtarou/items/a44ce783d09201fc28f5

$ sudo mysql_secure_installation
$ sudo mysql -u root -p

mysql> CREATE DATABASE test;
mysql> set global validate_password_policy=LOW;
mysql> CREATE USER '名前'@'localhost' IDENTIFIED BY 'パスワード';
mysql> grant all on test.* to ユーザ名@localhost;

Article構造体の再定義

type Article struct {
    gorm.Model
    Title string `json:"Title"`
    Desc string `json:"desc"`
    Content string `json:"content"`
}
type Articles []Article

IDやらタイムスタンプをGORMのモデル構造体に切り替えます。
スライスのArticlesはもう使いませんが一括で扱うところが出てくるので型として定義します。

データベース接続

func connectDB() (*gorm.DB, error) {
    db, err := gorm.Open("mysql", "ユーザ名:パスワード@/テーブル名?charset=utf8&parseTime=True&loc=Local")
    return db, err
}

func initDb() *gorm.DB {
    db, err := connectDB()
    if err != nil {
        log.Fatalln("データベースの接続に失敗しました。")
    }
    return db
}

/all

func returnAllArticles(w http.ResponseWriter, r *http.Request){
    db := initDb()
    var articles Articles
    db.Find(&articles)
    json.NewEncoder(w).Encode(articles)
}

先に定義した構造体をインスタンス化し、db.find関数ですべての行のデータを格納します。

/article/{id} GET

func idParamToUint(r *http.Request) uint {
    vars := mux.Vars(r)
    id, _ := strconv.Atoi(vars["id"])
    var uid uint = uint(id)
    return uid
}

func returnSingleArticle(w http.ResponseWriter, r *http.Request){
    uid := idParamToUint(r)
    db := initDb()
    var article Article
    db.Where("id = ?", uid).First(&article)
    json.NewEncoder(w).Encode(article)
}

urlパラメータのidはstring型ですが、MySQLに登録されているidはuint型です。そのためstring型をuint型に型変換する関数の定義をしています。
strconvパッケージのstringToUintは、uint64型がreturnされてしまうため使わず、いったんint型にしています。
それから、GORMのdb.Where関数つかってidが一致するarticleを一つだけ取り出します。

/article POST

func ParseJsonArticle(w http.ResponseWriter ,r *http.Request) Article {
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article
    json.Unmarshal(reqBody, &article)
    json.NewEncoder(w).Encode(article)
    return article
}

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    fmt.Println("called createNewArticle")
    db := initDb()
    article := ParseJsonArticle(w, r)
    db.Create(&article)
    if db.NewRecord(article) {
        log.Println("新規articleの保存に失敗しました。")
    }
}

まずは、関数として(updateでも再利用するため)リクエストボディで送られてくるJSONをパースし、構造体として扱えるようにします。
GORMのCreate関数でMySqlにあたらしい行を追加します。

※ 編集しました(2020/7/16)
log.Fatalln -> log.Printlnに変更しました。
データの保存に失敗したときにプログラムを終了させる必要はないと考えるからです。

/article/{id} PUT

func updateArticle(w http.ResponseWriter, r *http.Request){
    uid := idParamToUint(r)
    db := initDb()

    updatedArticle := ParseJsonArticle(w, r)

    var article Article
    db.Where("id = ?", uid).First(&article)

    article.Title = updatedArticle.Title
    article.Desc = updatedArticle.Desc
    article.Content = updatedArticle.Content
    db.Save(&article)
}

CRUDで最も複雑なupdateですが、今までの組み合わせで実現できました。ここでは、Article構造体を二つインスタンス化させています。
一つはMySQLから引っ張ってきたデータ用、もう一つはリクエストボディから更新後のデータとして入ってくるupdatedArticleで、一つ目のArticleに二つ目のupdatedArticleを代入後Save関数を呼び出しています。

/article/{id} DELETE

func deleteArticle(w http.ResponseWriter, r *http.Request) {
    uid := idParamToUint(r)
    db := initDb()
    db.Delete(Article{}, "id = ?", uid)
}

GORMのDelete関数によって簡単に実装できました:relaxed:

handleRequests関数

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    myRouter.HandleFunc("/article/{id}", returnSingleArticle).Methods("GET")
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    myRouter.HandleFunc("/article/{id}", updateArticle).Methods("PUT")
    myRouter.HandleFunc("/article/{id}", deleteArticle).Methods("DELETE")
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

ポイントは、"article/{id}" の.Methods("GET")省略していないところです。
仮に省略してしまうとPUTやDELETEをつけてcurlしても、すべてGETメソッドのreturnSingleArticle関数にルーティングされてしまいます。

まとめ

GORMとGorilla/muxパッケージでいい感じにRestAPIを作れた。
特にGORMに関しては、公式ページが日本語化されていて楽だった。
急いでクライアント側も作らなくては!

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

Go製インタプリタに見る動的値型まとめ

Go言語でインタプリタを作っているのだが、動的値型の宣言の仕方でぜんぜん実行速度が変わってくるので、メンテナンス性と実行速度いろいろ検討してみる。

構造体に全部の型を詰め込むパターン

動的値型を作る上で最も直感的に使えるのが全部の型を構造体に詰め込むパターン。あまり考えなくても使えるがメモリ効率はよくない。

struct.go
type Value struct {
    TypeNo int
    IntValue int
    StrValue string
}
// 必要な時に変換関数を呼ぶ
func (v *Value) ToInt() int {
  if TypeNo==TypeInt { return v.IntVaue }
  if TypeNo==TypeStr { return StrToInt(v.StrValue) }
}
func (v *Value) ToString() string { ... }

全てをinterface{}で表現するパターン

動的値型をinterface{}で表す方法。必要に応じて、任意の型にキャストして使う。キャストが面倒なので使い勝手は良くない上に間違えると落ちる。しかし、Go言語の機能を使うため動作速度は比較的早い。メモリも必要最小限。

if1.go
type Value interface{} 

// switchで型を分けて使う
func ToInt(v Value) int {
  switch v := v.(type) {
    case int: return v.(int)
    case string: return StrToInt(v.(string))
    default: return 0
  }
}
func ToString(v Value) string { ... }

Goの型を利用しつつinterfaceで変換メソッドを用意

Goの型を利用しつつもメソッドを追加して共通型として利用する。メモリ効率もよくinterfaceの良さを活かしつつ、それなりに安全に運用できる。Gopher-luaの作者の型はこの部分が動作ネックになっているとブログに書かれていたが、Go製のLuaの中ではGopher-luaは速い方。

if2.go
type Value interface {
  ValueType() int
  ToInt() int
  ToString() string
}

type MyInt int
func (p MyInt) ToInt() { return p }
func (p MyInt) ToString() { return IntToStr(p) }

type MyStr string
func (p MyStr) ToInt() { return StrToInt(p) }
func (p MyStr) ToString() { return p }

interface{}を利用しつつも構造体に閉じ込める

内部的には、interface{}を利用しつつも、構造体とそのメソッドと安全に動的値型を操作できる。ただし、動的値型を生成する際に、一枚皮を被せているので、インタプリタ実装時大きなオーバーヘッドが必要となる。この記事執筆時点なでしこGo版では、この方式を採用しているが、値型の生成がとにかく遅い。

structif.go
struct Value {
  ValueType int
  value interface{}
}

func (v *Value) ToInt() int {
  switch value.(type) {
    case int: return v.value.(int)
    case string: return StrToInt(v.value.(string))
  }
}

なお、筆者が作っている日本語プログラミング言語「なでしこ」のGo言語実装版はこちら

その他

その他、参考になりそうな点

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

【Go】SQLBoilerの使い方【ORM】

SQLBoiler とは

タイトル通り、Go言語のORMです。とりあえず一次ソース貼っておくので一緒に見ましょう。一次ソースです:santa:

GoのORMはGORMしか使用経験がありませんが、サクッと自分の思ったことをまとめました。
SQLBoilerを使用したソースコードはこちら

特徴

  • DBを作成してマンドを打つと構造体を自動で生成してくれて、InsertやQueryなどができるようになる
  • (メリット)他のORMよりパフォーマンスが良い(README参照)
  • (メリット)Goのstructとしても活用しやすい
  • (デメリット)ソースが少ない

実装手順

  1. DB作成
  2. DB接続
  3. 導入
  4. tomlファイル作成
  5. SQL文記述
  6. データのやりとり

開発環境

Mac OS Catalina 10.15.5
Go 1.14
MySQL 8.0.19

1.DB作成

(この記事にたどり着くような人は1と2は見なくてもサクッとできそうですが、、わかるわ。と思ったら手順3まで飛ばしてください。)

今回はMySQLを使用します。

をしてください。この手順にある処理はググったらいっぱい出てくると思うので詳しくは書きません。mysqlに入っていろいろするだけです。僕は結構優しいのでリンクは一応貼ってます。
権限付与はデータベースレベルで
grant all on DB名.* to 'ユーザー名' with grant option;
とかでいけると思います。

2. DB接続

次に、DBに接続します。

main.go
func New() *sql.DB {
  db, err := sql.Open("mysql", "ユーザー名:パスワード@/DB名?charset=utf8mb4&parseTime=true")
  if err != nil {
    panic(err)
  }
  return db
}

3.導入

ターミナル
go get -u github.com/volatiletech/sqlboiler
go get github.com/volatiletech/sqlboiler/drivers/sqlboiler-mysql

4.tomlファイル作成

ルートディレクトリにtomlファイルを作ります。

ターミナル
touch sqlboiler.toml

必要事項を記述します。

  • pkgname...作成したいパッケージ名を指定
  • output...ルートディレクトリから見て、ファイルを生成したいディレクトリの場所
  • dbname, user, pass ...DB名、ユーザー名、パスワード
  • [mysql]は使用しているデータベースの種類を指定しています
sqlboiler.toml
pkgname="entity"
output="domain/entity"
[mysql]
  dbname = "exampledb"
  host   = "localhost"
  port   = 3306
  user   = "root"
  pass   = "password"
  sslmode= "false"

5.SQL文記述

SQLの文法をもとに下のようなファイルを作成しました。記事も後半になって僕が優しくなくなってきたので解説はしません。
関係は user:review が1:多で、CONSTRAINT..の部分で外部キー接続もしています。

schema.sql
CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `reviews` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `text` text NOT NULL,
  `user_id` bigint NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `user_reviews_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ユーザー名、パスワード、DB名を入れてスキーマを作成し、

ターミナル
$ mysqldef -uroot -ppassword exampledb < schema.sql

実行します。

$ sqlboiler mysql

上記2つのコマンドはSQLに変更を加えるときに使うので、Makefileとかのタスクランナー で自動化しておくと良いと思います。

自分の指定したディレクトリ以下に大量のファイルが生成されたら成功です。自分の作成したテーブルのファイルを見てるとこんなんがあると思います。

users.go
// 省略

type User struct {
    ID        int64     `boil:"id" json:"id" toml:"id" yaml:"id"`
    Name      string    `boil:"name" json:"name" toml:"name" yaml:"name"`
    CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
    UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
    DeletedAt null.Time `boil:"deleted_at" json:"deleted_at,omitempty" toml:"deleted_at" yaml:"deleted_at,omitempty"`

    R *userR `boil:"-" json:"-" toml:"-" yaml:"-"`
    L userL  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

// 省略

6. データのやり取り

ファイルの生成ができたら、いよいよデータのやりとりを行います。
INSERTもできますが今回はSELECTを扱います。事前にデータを入れているReviewsテーブルの(削除されていない)id=1のデータを取ってきます。
(ルーティングの記述は省略しています。)

main.go
  func handler(w http.ResponseWriter, r *http.Request) {
    conn := db.New()
    ctx := r.Context()
    var id int64 = 1
    review, err := entity.Reviews(
        // entityパッケージのReviewsテーブルからIDカラムを条件にidと等しいデータをSELECT
        entity.ReviewWhere.ID.EQ(id),
        entity.ReviewWhere.DeletedAt.IsNull(),
    ).One(ctx, conn)
    if err != nil {
        fmt.println(err)
        return
    }
    fmt.Println("----------review----------")
    fmt.Println(review)
    return
}

詳しい文法については一次ソースを参照してもらえるとわかるのではないかと思います。(上の例はQueryBuildingあたりに書いてる)

あとは実行すると、Reviewがprintされるはずです。

ターミナル
$ go run main go

手順としては終了ですが、最後に外部キーの例だけ載せておきます。
上記例でDBで外部キーで紐づけている前提で、

main.go
    review, err := entity.Reviews(
        entity.ReviewWhere.ID.EQ(id),
        entity.ReviewWhere.DeletedAt.IsNull(),
        qm.Load("User")
    ).One(ctx, conn)

 fmt.Println(review.R.User)

とすると、UsersテーブルのUserをEagerLoadingで取ってくることができます。(has_manyの関係なら複数形にする)
以上になります。

間違い誤字脱字等あればご指摘いただけると幸いです?‍♂️

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

GoでGORMとTwitterAPIを使ったミニミニアプリ作ってみた

プログラミング歴3ヶ月目に突入しました!
GoでGORMとTwitterAPIを使ったアプリと言っていいのかすらわからないアプリを作りました!
GitHubに置いてあるのでよかったら見てください!(https://github.com/souhub/buzz_tweet)
そんなミニミニミニアプリですが、Railsでツイッター風アプリを作ったぶりの達成感とワクワクを感じることができました。
TwitterAPIを使って任意のユーザーの直近100件のツイート(リプとRTを含む)を取得して、GORMでデータベース操作していいねやRTが多い順や少ない順に並び替えるアプリです。

こんな感じ

これがホーム画面でここでツイートを並べ替えたい人のユーザーIDを入力し、何順に並びかえるか選択します。
スクリーンショット 2020-07-05 16.51.20.png
ここでは例として安倍首相のツイートをリツイートが多い順に並び替えるを選択しました。
スクリーンショット 2020-07-05 16.51.45.png
そして検索ボタンを押すとこのように並び替えられたページに移動します。
写真なのでこの下が全く見えないですが、スクロールするとズラーっと並んでいます。
スクリーンショット 2020-07-05 16.52.34.png

反省点

  • 並び替える直近100件をその人がツイートしたもののみ(リプ、リツイートしたものを含まない)にしたかったができなかった点。
  • 一度検索してホーム画面に戻ったらデータベースを削除するようにしたかったが、アプリを落とさない限りデータベースを削除できなかった点。
  • CSSファイルを別ファイルに書いて読み込もうとしたができなかった点。笑

感想

初めてAPIを使ったアプリを作成したが本当に時間を忘れるくらい楽しかったです。
自分の思い通りに作成できなかった点がいくつかあり悔しかったのでもっと勉強して使いこなせるようになりたいです。

参考

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

go修行19日目 データベース

データベース作成

INSERT

package main

import (
    "database/sql"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードINSERT
    cmd = "INSERT INTO person (name, age) VALUES (?, ?)"
    _, err = DbConnection.Exec(cmd, "Nancy", 20)
    if err != nil {
        log.Fatalln(err)
    }
}

PS C:\Users\yuta\go\src\semaphore> sqlite3 .\example.sql
SQLite version 3.32.3 2020-06-18 14:00:33
Enter ".help" for usage hints.
sqlite> .table
person
sqlite>
sqlite> select * from person;
Nancy|20

SELECT

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

type Person struct {
    Name string
    Age  int
}

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードINSERT
    cmd = "SELECT * FROM person where age = ?"
    row := DbConnection.QueryRow(cmd, 20)
    var p Person
    err = row.Scan(&p.Name, &p.Age)
    if err != nil {
        if err == sql.ErrNoRows {
            log.Println("No row")
        } else {
            log.Println(err)
        }
    }
    fmt.Println(p.Name, p.Age)
}

Nancy 20
```

DELETE

package main

import (
    "database/sql"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

type Person struct {
    Name string
    Age  int
}

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードDELETE
    cmd = "DELETE FROM person WHERE name = ?"
    _, err = DbConnection.Exec(cmd, "Nancy")
    if err != nil {
        log.Fatalln(err)
    }
}

教材

https://www.udemy.com/course/go-fintech/learn/lecture/12088980

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

go修行20日目 データベース

データベース作成

INSERT

package main

import (
    "database/sql"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードINSERT
    cmd = "INSERT INTO person (name, age) VALUES (?, ?)"
    _, err = DbConnection.Exec(cmd, "Nancy", 20)
    if err != nil {
        log.Fatalln(err)
    }
}

PS C:\Users\yuta\go\src\semaphore> sqlite3 .\example.sql
SQLite version 3.32.3 2020-06-18 14:00:33
Enter ".help" for usage hints.
sqlite> .table
person
sqlite>
sqlite> select * from person;
Nancy|20

SELECT

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

type Person struct {
    Name string
    Age  int
}

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードINSERT
    cmd = "SELECT * FROM person where age = ?"
    row := DbConnection.QueryRow(cmd, 20)
    var p Person
    err = row.Scan(&p.Name, &p.Age)
    if err != nil {
        if err == sql.ErrNoRows {
            log.Println("No row")
        } else {
            log.Println(err)
        }
    }
    fmt.Println(p.Name, p.Age)
}

Nancy 20
```

DELETE

package main

import (
    "database/sql"
    "log"

    _ "github.com/mattn/go-sqLite3"
)

var DbConnection *sql.DB

type Person struct {
    Name string
    Age  int
}

func main() {
    DbConnection, _ := sql.Open("sqlite3", "./example.sql")
    defer DbConnection.Close()

    // テーブル作成
    cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age INT)`
    _, err := DbConnection.Exec(cmd)
    // エラーハンドリング
    if err != nil {
        log.Fatalln(err)
    }

    // レコードDELETE
    cmd = "DELETE FROM person WHERE name = ?"
    _, err = DbConnection.Exec(cmd, "Nancy")
    if err != nil {
        log.Fatalln(err)
    }
}

教材

https://www.udemy.com/course/go-fintech/learn/lecture/12088980

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