亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何達到接口類型的結構成員

如何達到接口類型的結構成員

Go
侃侃爾雅 2023-03-07 11:07:15
我必須在切片中保留多類型結構并為它們播種。我接受了接口類型的可變參數并 foreach 它們。如果我調用接口的方法,它就可以工作,但是當我試圖訪問結構時,我不能。我該如何解決?注意:Seed() 方法返回數據的文件名。界面:type Seeder interface {    Seed() string}方法:func (AirportCodes) Seed() string {    return "airport_codes.json"}播種機切片:seederModelList = []globals.Seeder{        m.AirportCodes{},        m.Term{},    }最后一個,SeedSchema 函數:func (db *Database) SeedSchema(models ...globals.Seeder) error {    var (        subjects []globals.Seeder        fileByte []byte        err      error        // tempMember map[string]interface{}    )    if len(models) == 0 {        subjects = seederModelList    } else {        subjects = models    }    for _, model := range subjects {        fileName := model.Seed()        fmt.Printf("%+v\n", model)        if fileByte, err = os.ReadFile("db/seeds/" + fileName); err != nil {            fmt.Println("asd", err)            // return err        }        if err = json.Unmarshal(fileByte, &model); err != nil {            fmt.Println("dsa", err)            // return err        }        modelType := reflect.TypeOf(model).Elem()        modelPtr2 := reflect.New(modelType)        fmt.Printf("%s\n", modelPtr2)     }    return nil}我可以達到精確的模型,但無法創建成員和種子。
查看完整描述

1 回答

?
開滿天機

TA貢獻1786條經驗 獲得超13個贊

在評論中反復討論之后,我將在此處發布這個最小的答案。這絕不是一個明確的“這就是你所做的”類型的答案,但我希望這至少可以為你提供足夠的信息來幫助你開始。為了達到這一點,我根據您提供的代碼片段做出了一些假設,并且我假設您希望通過某種命令(例如)為數據庫播種your_bin seed。這意味著做出了以下假設:

  1. 存在模式和相應的模型/類型(類似AirportCodes等)

  2. 每種類型都有自己的源文件(名稱來自Seed()方法,返回一個.json文件名)

  3. 因此,假定種子數據的格式類似于[{"seed": "data"}, {"more": "data"}].

  4. 可以附加種子文件,如果模式發生變化,種子文件中的數據也可以一起更改。這在 ATM 中不太重要,但仍然是一個應該注意的假設。

好的,讓我們首先將所有 JSON 文件移動到可預測的位置。在一個相當大的真實世界應用程序中,您會使用類似XDG 基本路徑的東西,但為了簡潔起見,我們假設您在一個臨時容器中運行它,并且/所有相關資產都已復制到所述容器中。

將所有種子文件放在seed_data目錄下的基本路徑中是有意義的。每個文件都包含特定表的種子數據,因此一個文件中的所有數據都整齊地映射到一個模型上。讓我們暫時忽略關系數據。我們只是假設,現在,這些文件中的數據至少在內部是一致的,并且任何X-to-X關系數據都必須具有正確的 ID 字段,允許 JOIN 等。


開始吧

所以我們有了我們的模型,以及 JSON 文件中的數據。現在我們可以只創建所述模型的一部分,確保在插入其他數據之前您想要/需要存在的數據表示為比另一個更高的條目(較低的索引)。有點像這樣:

seederModelList = []globals.Seeder{

    m.AirportCodes{}, // seeds before Term

    m.Term{},         // seeds after AirportCodes

}

但是,或者從這個方法返回文件名Seed,為什么不傳入連接并讓模型像這樣處理自己的數據:


func (_ AirportCodes) Seed(db *gorm.DB) error {

    // we know what file this model uses

    data, err := os.ReadFile("seed_data/airport_codes.json")

    if err != nil {

        return err

    }

    // we have the data, we can unmarshal it as AirportCode instances

    codes := []*AirportCodes{}

    if err := json.Unmarshal(data, &codes); err != nil {

        return err

    }

    // now INSERT, UPDATE, or UPSERT:

    db.Clauses(clause.OnConflict{

        UpdateAll: true,

    }).Create(&codes)

}

對其他模型執行相同的操作,例如Terms:


func (_ Terms) Seed(db *gorm.DB) error {

    // we know what file this model uses

    data, err := os.ReadFile("seed_data/terms.json")

    if err != nil {

        return err

    }

    // we have the data, we can unmarshal it as Terms instances

    terms := []*Terms{}

    if err := json.Unmarshal(data, &terms); err != nil {

        return err

    }

    // now INSERT, UPDATE, or UPSERT:

    return db.Clauses(clause.OnConflict{

        UpdateAll: true,

    }).Create(&terms)

}

當然,考慮到我們在模型中有數據庫訪問權限,這確實會導致一些混亂,如果你問我的話,它實際上應該只是一個 DTO。這在錯誤處理方面也有很多不足之處,但它的基本要點是:


func main() {

    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // omitted error handling for brevity

    seeds := []interface{

        Seed(*gorm.DB) error

    }{

        model.AirportCodes{},

        model.Terms{},

        // etc...

    }

    for _, m := range seeds {

        if err := m.Seed(db); err != nil {

            panic(err)

        }

    }

    db.Close()

}

好的,這應該讓我們開始了,但讓我們通過以下方式將這一切變成更好的東西:

  1. 將整個數據庫交互移出 DTO/模型

  2. 將事情包裝到事務中,這樣我們就可以回滾錯誤

  3. 稍微更新初始切片以使事情更清晰

因此,如前所述,我假設您有存儲庫之類的東西來處理單獨包中的數據庫交互。我們不應該調用Seed模型并將數據庫連接傳遞給模型,而應該依賴我們的存儲庫:

db, _ := gorm.Open() // same as before

acs := repo.NewAirportCodes(db) // pass in connection

tms := repo.NewTerms(db) // again...

現在我們的模型仍然可以返回 JSON 文件名,或者我們可以將其作為constrepos 中的一個。在這一點上,這并不重要。最主要的是,我們可以在存儲庫中完成實際的數據插入。


如果你愿意,你可以將你的種子切片更改為這樣的東西:


calls := []func() error{

    acs.Seed, // assuming your repo has a Seed function that does what it's supposed to do

    tms.Seed,

}

然后循環執行所有播種:


for _, c := range calls {

    if err := c(); err != nil {

        panic(err)

    }

}

現在,這只剩下交易問題了。值得慶幸的是,gorm 使這變得非常簡單:


db, _ := gorm.Open()

db.Transaction(func(tx *gorm.DB) error {

    acs := repo.NewAirportCodes(tx) // create repo's, but use TX for connection

    if err := acs.Seed(); err != nil {

        return err // returning an error will automatically rollback the transaction

    }

    tms := repo.NewTerms(tx)

    if err := tms.Seed(); err != nil {

        return err

    }

    return nil // commit transaction

})

您可以在這里擺弄更多的東西,例如創建可以單獨提交的相關數據批次,您可以添加更精確的錯誤處理和更多信息的日志記錄,更好地處理沖突(區分 CREATE 和 UPDATE 等...)。不過,最重要的是,有一點值得牢記:


Gorm有一個遷移系統

我不得不承認,我已經有一段時間沒有處理 gorm 了,但是 IIRC,如果模型發生變化,你可以讓表自動遷移,并在啟動時運行自定義 go 代碼和/或 SQL 文件,這些都可以使用,相當容易地播種數據。可能值得研究一下它的可行性……


查看完整回答
反對 回復 2023-03-07
  • 1 回答
  • 0 關注
  • 115 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號