模型Account包含嵌套 struct -CurrencyUser

当我在DB中创建一个Account的新实例,然后在我的响应中返回它时,嵌套的实体为空:


type Account struct {
    BaseModel
    Name       string          `gorm:"size:64;not null" json:"name"`
    Balance    decimal.Decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"`
    UserID     int             `gorm:"not null" json:"-"`
    User       User            `gorm:"foreignKey:UserID" json:"user"`
    CurrencyID int             `gorm:"not null" json:"-"`
    Currency   Currency        `gorm:"foreignKey:CurrencyID" json:"currency"`
}

type CreateAccountBody struct {
    Name       string          `json:"name" binding:"required"`
    Balance    decimal.Decimal `json:"balance"`
    CurrencyID int             `json:"currency_id" binding:"required"`
}

func CreateAccount(ctx *gin.Context) {
    body := CreateAccountBody{}

    if err := ctx.Bind(&body); err != nil {
        log.Println("Error while binding body:", err)
        ctx.JSON(
            http.StatusBadRequest,
            gin.H{"error": "Wrong request parameters"},
        )
        return
    }

    account := Account {
        Name:       body.Name,
        Balance:    body.Balance,
        CurrencyID: body.CurrencyID,
        UserID:     1,
    }
    
    if result := db.DB.Create(&account); result.Error != nil {
        log.Println("Unable to create an account:", result.Error)
    }    

    ctx.JSON(http.StatusCreated, gin.H{"data": account})
}


为了避免这个问题,我用单独的查询刷新了帐户变量:

db.DB.Create(&account)
db.DB.Preload("User").Preload("Currency").Find(&account, account.ID)
ctx.JSON(http.StatusCreated, gin.H{"data": account})

这是达到预期效果的最有效、最正确的方法吗?

推荐答案

我会和你分享我通常是如何处理这种情况的.首先,让我来分享代码.

main.go file

package main

import (
    "context"

    "gogindemo/handlers"

    "github.com/gin-gonic/gin"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

var (
    db  *gorm.DB
    ctx *gin.Context
)

func init() {
    dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
    var err error
    db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(err)
    }

    db.AutoMigrate(&handlers.Currency{})
    db.AutoMigrate(&handlers.User{})
    db.AutoMigrate(&handlers.Account{})
}

func AddDb() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        ctx.Request = ctx.Request.WithContext(context.WithValue(ctx.Request.Context(), "DB", db))
        ctx.Next()
    }
}

func main() {
    db.Create(&handlers.User{Id: 1, Name: "john doe"})
    db.Create(&handlers.User{Id: 2, Name: "mary hut"})
    db.Create(&handlers.Currency{Id: 1, Name: "EUR"})
    db.Create(&handlers.Currency{Id: 2, Name: "USD"})

    r := gin.Default()
    r.POST("/account", AddDb(), handlers.CreateAccount)

    r.Run()
}

在这里,我刚刚添加了 bootstrap 数据库对象的代码,并向其中添加了一些伪数据.

handlers/handlers.go file

package handlers

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/shopspring/decimal"
    "gorm.io/gorm"
)

type User struct {
    Id   int
    Name string
}

type Currency struct {
    Id   int
    Name string
}

type Account struct {
    Id         int
    Name       string          `gorm:"size:64;not null" json:"name"`
    Balance    decimal.Decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"`
    UserID     int             `gorm:"not null" json:"-"`
    User       User            `gorm:"foreignKey:UserID" json:"user"`
    CurrencyID int             `gorm:"not null" json:"-"`
    Currency   Currency        `gorm:"foreignKey:CurrencyID" json:"currency"`
}

type CreateAccountBody struct {
    Name       string          `json:"name" binding:"required"`
    Balance    decimal.Decimal `json:"balance"`
    CurrencyID int             `json:"currency_id" binding:"required"`
}

func CreateAccount(c *gin.Context) {
    db, ok := c.Request.Context().Value("DB").(*gorm.DB)
    if !ok {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
        return
    }
    var accountReq CreateAccountBody
    if err := c.BindJSON(&accountReq); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "wrong request body payload"})
        return
    }

    // create Account & update the "account" variable
    account := Account{Name: accountReq.Name, Balance: accountReq.Balance, CurrencyID: accountReq.CurrencyID, UserID: 1}
    db.Create(&account).Preload("Currency").Preload("User").Find(&account, account.Id)

    c.IndentedJSON(http.StatusCreated, account)
}

Within this file, I actually talk with the database through the DB passed in the context. Now, back to your question.
If the relationship between the Currency/Account and User/Account is of type 1:1, then, you should rely on the Preload clause. This will load the related entity in a separate query instead of adding it in an INNER JOIN clause.

如果这解决了你的问题,请让我知道,谢谢!

Go相关问答推荐

如何在AWS SDK Go v2 STS上正确使用重试

为什么Slices包中的函数定义Slice参数的类型参数?

使用Digitorus/pdfsign在GO(Golang)中签署pdf文件

如何从 nil 指针创建值

Go 中带有回调的 MiniDumpWriteDump

如何绕过深层 xml,没有嵌套循环?

git ls-remote 成功而 go get 失败

无法使用带有 422 的 go-github 创建提交 - 更新不是快进

尽管存在 WaitGroup,Goroutines 似乎被打断了

使用 Go 根据 /etc/shadow 文件中的散列密码验证密码

如何使用 go-git 将特定分支推送到远程

类型/ struct 函数的 GoDoc 示例函数

如果值为 false,gRPC 不返回布尔值

每次有人进入我的网站时如何运行特定功能?

在 GORM 中,如何在特定时区配置 autoCreateTime 和 autoUpdateTime?

如何在 GORM 中迭代一个 int 数组

Golang SSH客户端错误无法验证,try 的方法[无公钥],没有支持的方法

函数参数的判断顺序是什么?

Golang LinkedList 删除第一个元素

为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致panic ?