指定`gorm:”unique”`会导致预加载属于关系的问题。

huangapple go评论82阅读模式
英文:

Specifying `gorm:"unique"`, causing preload of belongs to relation a problem

问题

这是我的帖子和用户结构模型:

type Post struct {
    ID     string `gorm:"primaryKey"`
    Title  string `gorm:"unique"`
    UserID string
    User   User
}

type User struct {
    ID   string `gorm:"primaryKey"`
    Name string
}

// 创建一个具有标题和用户输入的帖子的代码
func CreatePost(title string, u User) (Post, error) {
    b := Post{Title: title, User: u}
    dbIns := GetDB()
    if err := dbIns.Create(&b).Error; err != nil {
        return Post{}, err
    }
    return b, nil
}

// 获取帖子的代码,同时预加载其用户字段
func GetOnePost() (Post, error) {
    var p Post
    dbIns := GetDB()
    if err := dbIns.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
        return Post{}, err
    }
    return p, nil
}

这是从GetOnePost()获得的输出,正是我所期望的,Post.User不为空。

2022/11/27 15:42:48 帖子标题John-Doe的标题UUID32158510-6dcc-4c26-bcb8-7d76a88bda72作者UUID73d020d3-eb8d-4bf7-9f57-e821c5deceed
2022/11/27 15:42:48 用户名John-DoeUUID73d020d3-eb8d-4bf7-9f57-e821c5deceed

然而,当我将User.Name指定为唯一时(因为我希望每个用户具有不同的名称),GetOnePost()的输出发生了变化。请忽略UUID。

2022/11/27 15:57:49 帖子标题John-Doe的标题UUID26355816-c376-497a-a250-be118c5933e2作者UUIDe858cd72-56b9-4884-b6e6-94121c36719b
2022/11/27 15:57:49 用户名:,UUID

问题在于,当我向User模型添加唯一约束时,返回的Post.User似乎为空。如果我从User模型中删除唯一约束,它就恢复正常(Post.User非空)。所以我认为问题出在gorm:"unique"这部分。

英文:

Here's my Post & User struct model :

type Post struct {
    ID     string `gorm:"primaryKey"`
    Title  string `gorm:"unique"`
    UserID string
    User   User
}


type User struct {
    ID   string `gorm:"primaryKey"`
    Name string
}

// Code to create a Post with title & user input
func CreatePost(title string, u User) (Post, error) {
	b := Post{Title: title, User: u}
	dbIns := GetDB()
	if err := dbIns.Create(&b).Error; err != nil {
		return Post{}, err
	}
	return b, nil
}

// Code to get a Post, also preload its User field
func GetOnePost() (Post, error) {
	var p Post
	dbIns := GetDB()
	if err := dbIns.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
		return Post{}, err
	}
	return p, nil
}

Here's is an output of what I got from GetOnePost(), which is exactly what I expected, Post.User is not empty.

2022/11/27 15:42:48 Post title: John-Doe's Title, UUID: 32158510-6dcc-4c26-bcb8-7d76a88bda72, authorUUID: 73d020d3-eb8d-4bf7-9f57-e821c5deceed 
2022/11/27 15:42:48 User name: John-Doe, UUID: 73d020d3-eb8d-4bf7-9f57-e821c5deceed

However when I specified the User.Name to be unique (since I want each user to have different Name)<br/>
note : yes I did migrate into a new table.

type User struct {
	ID   string `gorm:&quot;primaryKey&quot;`
	Name string `gorm:&quot;unique&quot;`
}

The output from GetOnePost() changed to this : (please ignore the UUID)

2022/11/27 15:57:49 Post title: John-Doe&#39;s Title, UUID: 26355816-c376-497a-a250-be118c5933e2, authorUUID: e858cd72-56b9-4884-b6e6-94121c36719b 
2022/11/27 15:57:49 User name: , UUID:

A problem is that the returned Post.User seems to be empty, when I add unique constraint to User model.

If I remove the unique constraint from User model, it returns to normal (Post.User not-empty). So it seems to me that the problem is with the gorm:"unique" part.

答案1

得分: 1

老实说,我无法重现你的问题。不过,我添加了我尝试重现你的问题的代码,它按预期工作。首先让我展示代码,然后我会给你一个简要的解释:

package main

import (
	"fmt"

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

	"github.com/google/uuid"
	_ "github.com/lib/pq"
)

type Post struct {
	ID     string `gorm:"primaryKey"`
	Title  string `gorm:"unique"`
	UserID string
	User   User
}

type User struct {
	ID   string `gorm:"primaryKey"`
	Name string `gorm:"unique"`
}

// 创建一个带有标题和用户输入的帖子的代码
func CreatePost(db *gorm.DB, title string, u User) (Post, error) {
	id := uuid.NewString()
	b := Post{ID: id, Title: title, User: u}
	if err := db.Create(&b).Error; err != nil {
		return Post{}, err
	}
	return b, nil
}

// 获取一篇帖子的代码,同时预加载其User字段
func GetOnePost(db *gorm.DB) (Post, error) {
	var p Post
	if err := db.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
		return Post{}, err
	}
	return p, nil
}

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

	db.AutoMigrate(&User{})
	db.AutoMigrate(&Post{})

	defer db.Exec("DROP TABLE users")
	defer db.Exec("DROP TABLE posts")

	userId := uuid.NewString()

	_, err = CreatePost(db, "Learn Go", User{
		ID:   userId,
		Name: "John Doe",
	})
	if err != nil {
		panic(err)
	}

	post, err := GetOnePost(db)
	if err != nil {
		panic(err)
	}

	// 这会导致数据库中的重复错误 SQLSTATE 23505
	// if err := db.Create(&User{
	// 	ID:   uuid.NewString(),
	// 	Name: "John Doe",
	// }).Error; err != nil {
	// 	panic(err)
	// }

	fmt.Printf("帖子的详细信息:%v\n", post)
}

代码基本上与你提供的代码相同。我添加了两个延迟函数调用,以便在每次运行代码后删除表。通过这样做,我们始终确保使用最新的表定义。然后,我在Postgres中检查了UNIQUE约束是否存在,并且我正确地找到了它。然而,当我尝试运行我的代码时,我成功地能够检索到User信息。如果你尝试取消注释以// this leads to a Duplicate error SQLSTATE 23505 in DB开头的代码块,你将会得到一个重复错误,因此约束在数据库端得到了执行。

如果你使用我的解决方案,让我知道你的代码是否也能正常工作!

英文:

Honestly, I was not able to repro your issue. Anyway, I added my attempt to repro your issue that works as expected. Let me show the code first and then I'll provide you with a brief explanation:

package main

import (
	&quot;fmt&quot;

	&quot;gorm.io/driver/postgres&quot;
	&quot;gorm.io/gorm&quot;

	&quot;github.com/google/uuid&quot;
	_ &quot;github.com/lib/pq&quot;
)

type Post struct {
	ID     string `gorm:&quot;primaryKey&quot;`
	Title  string `gorm:&quot;unique&quot;`
	UserID string
	User   User
}

type User struct {
	ID   string `gorm:&quot;primaryKey&quot;`
	Name string `gorm:&quot;unique&quot;`
}

// Code to create a Post with title &amp; user input
func CreatePost(db *gorm.DB, title string, u User) (Post, error) {
	id := uuid.NewString()
	b := Post{ID: id, Title: title, User: u}
	if err := db.Create(&amp;b).Error; err != nil {
		return Post{}, err
	}
	return b, nil
}

// Code to get a Post, also preload its User field
func GetOnePost(db *gorm.DB) (Post, error) {
	var p Post
	if err := db.Model(&amp;Post{}).Preload(&quot;User&quot;).First(&amp;p).Error; err != nil {
		return Post{}, err
	}
	return p, nil
}

func main() {
	dsn := &quot;host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable&quot;
	db, err := gorm.Open(postgres.Open(dsn), &amp;gorm.Config{})
	if err != nil {
		panic(err)
	}

	db.AutoMigrate(&amp;User{})
	db.AutoMigrate(&amp;Post{})

	defer db.Exec(&quot;DROP TABLE users&quot;)
	defer db.Exec(&quot;DROP TABLE posts&quot;)

	userId := uuid.NewString()

	_, err = CreatePost(db, &quot;Learn Go&quot;, User{
		ID:   userId,
		Name: &quot;John Doe&quot;,
	})
	if err != nil {
		panic(err)
	}

	post, err := GetOnePost(db)
	if err != nil {
		panic(err)
	}

	// this leads to a Duplicate error SQLSTATE 23505 in DB
	// if err := db.Create(&amp;User{
	// 	ID:   uuid.NewString(),
	// 	Name: &quot;John Doe&quot;,
	// }).Error; err != nil {
	// 	panic(err)
	// }

	fmt.Printf(&quot;post&#39;s details: %v\n&quot;, post)
}

More or less the code is the same as you provided. I added two deferred function calls to delete the tables after every run of the code. Thanks to this we're always sure to work with the latest table definition provided. Then, I check in Postgres if the UNIQUE constraint was present and I correctly found it. However, when I try to run my code I was successfully able to retrieve also the User information. If you try to uncomment the code block starting with this comment // this leads to a Duplicate error SQLSTATE 23505 in DB you'll get a duplicate error so the constraint is enforced on the database side.

Let me know if by using my solution your code works too!

huangapple
  • 本文由 发表于 2022年11月27日 11:06:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/74587204.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定