英文:
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的标题,UUID:32158510-6dcc-4c26-bcb8-7d76a88bda72,作者UUID:73d020d3-eb8d-4bf7-9f57-e821c5deceed
2022/11/27 15:42:48 用户名:John-Doe,UUID:73d020d3-eb8d-4bf7-9f57-e821c5deceed
然而,当我将User.Name指定为唯一时(因为我希望每个用户具有不同的名称),GetOnePost()的输出发生了变化。请忽略UUID。
2022/11/27 15:57:49 帖子标题:John-Doe的标题,UUID:26355816-c376-497a-a250-be118c5933e2,作者UUID:e858cd72-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:"primaryKey"`
Name string `gorm:"unique"`
}
The output from GetOnePost() changed to this : (please ignore the UUID)
2022/11/27 15:57:49 Post title: John-Doe'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 (
"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"`
}
// Code to create a Post with title & 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(&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(&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)
}
// this leads to a Duplicate error SQLSTATE 23505 in DB
// if err := db.Create(&User{
// ID: uuid.NewString(),
// Name: "John Doe",
// }).Error; err != nil {
// panic(err)
// }
fmt.Printf("post's details: %v\n", 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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论