从两个不同的结构体中获取数据

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

Get data from two different struct

问题

这是一个关于用户(User)和帖子(Post)的结构体。我试图在用户创建新帖子时,将用户的姓名(Name)包含在帖子结构体中。

type User struct {
    ID      int
    Name    string
    Created time.Time
}

type Post struct {
    ID        int
    PostTitle string
    PostDesc  string
    Created   time.Time
}

你想知道如何在这两个结构体之间建立联系,比如将帖子的作者与用户关联起来。

目标是通过以下代码获取帖子作者的姓名:

post, err := app.Models.Posts.GetPost(id)

GetPost() 函数只是执行 SELECT 查询并扫描行。

英文:

Have this struct of User and Post and I try to make Name from User to be included within Post Struct when a user create a new post.

type User struct {
	ID             int
	Name           string
	Created        time.Time
}

type Post struct {
	ID int
	PostTitle string
	PostDesc string
	Created time.Time
}

How can I create something connected between this two struct such as Author of the Post ?

The goal is try to get the name of the post author which from User struct with the code below:

post, err := app.Models.Posts.GetPost(id)

GetPost() just run SELECT query and scan row

答案1

得分: 1

试试这个解决方案,也许可以解决你的问题。

首先,我以这种方式定义了结构体:

// "Post" 属于 "User","UserID" 是外键
type Post struct {
    gorm.Model
    ID        int
    PostTitle string
    PostDesc  string
    Created   time.Time
    UserID    int
    User      User
}

type User struct {
    ID      int
    Name    string
    Created time.Time
}

通过这种方式,你可以说 Post 属于 User,并且在 Post 结构体内部访问用户的信息。
要查询记录,你需要使用 Preload("User") 来确保从单独的表中预加载用户记录。

注意在 Preload 中传递的参数名称,可能会有些棘手。

最后,你将能够访问嵌入结构体中的数据(使用 表示法)。
下面是一个完整的工作示例(使用 Docker 实现):

package main

import (
    "fmt"
    "time"

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

// "Post" 属于 "User","UserID" 是外键
type Post struct {
    gorm.Model
    ID        int
    PostTitle string
    PostDesc  string
    Created   time.Time
    UserID    int
    User      User
}

type User struct {
    ID      int
    Name    string
    Created time.Time
}

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(&Post{})

    newPost := &Post{ID: 1, PostTitle: "Golang", PostDesc: "Introduction to Golang", Created: time.Now(), UserID: 1, User: User{ID: 1, Name: "John Doe", Created: time.Now()}}
    db.Create(newPost)

    var post Post
    db.Preload("User").Find(&post, 1)
    fmt.Printf("author name: %q\n", post.User.Name)
}

如果我回答了你的问题,请告诉我!

英文:

Give it a try to this solution, maybe it resolves your issue.

First, I defined the structs in this way:

// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
	gorm.Model
	ID        int
	PostTitle string
	PostDesc  string
	Created   time.Time
	UserID    int
	User      User
}

type User struct {
	ID      int
	Name    string
	Created time.Time
}

In this way, you can say that Post belongs to User and access the User's information within the Post struct.
To query the records, you've to use Preload("User") to be sure to eager load the User records from the separate table.
> Keep attention to the name you pass in as the argument in Preload, as it can be tricky.

Lastly, you'll be able to access data in the embedded struct (with the dot notation).
Below, you can find a complete working example (implemented with the use of Docker):

package main

import (
	"fmt"
	"time"

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

// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
	gorm.Model
	ID        int
	PostTitle string
	PostDesc  string
	Created   time.Time
	UserID    int
	User      User
}

type User struct {
	ID      int
	Name    string
	Created time.Time
}

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(&Post{})

	newPost := &Post{ID: 1, PostTitle: "Golang", PostDesc: "Introduction to Golang", Created: time.Now(), UserID: 1, User: User{ID: 1, Name: "John Doe", Created: time.Now()}}
	db.Create(newPost)

	var post Post
	db.Preload("User").Find(&post, 1)
	fmt.Printf("author name: %q\n", post.User.Name)
}

Let me know if I answered your question!

答案2

得分: 1

这种方法没有使用任何ORM(对象关系映射)。这是一个简单的查询,可以返回多行结果。你需要扫描整个结果集,并将每一列映射到结构体的字段上。

记得始终检查错误。

以下是解决方案:

package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/lib/pq"
)

type Post struct {
	ID        int
	PostTitle string
	PostDesc  string
	Created   time.Time
	UserID    int
	User      User
}

type User struct {
	ID      int
	Name    string
	Created time.Time
}

func main() {
	conn, err := sql.Open("postgres", "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable")
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 获取最大的id
	var id int
	conn.QueryRow(`SELECT MAX(id) FROM posts`).Scan(&id)

	// 插入数据
	sqlInsertStmt := `INSERT INTO posts (id, post_title, post_desc, created, user_id) VALUES ($1,$2,$3,$4,$5)`
	if _, err = conn.Exec(sqlInsertStmt, id+1, "TDD", "Introduction to Test-Driven-Development", time.Now(), 1); err != nil {
		panic(err)
	}

	// 读取数据
	rows, err := conn.Query(`SELECT posts.id, post_title, post_desc, posts.created, users.id, users.name, users.created FROM posts INNER JOIN users ON posts.user_id=users.id`)
	if err != nil {
		panic(err)
	}

	var posts []Post
	for rows.Next() {
		var post Post
		if err = rows.Scan(&post.ID, &post.PostTitle, &post.PostDesc, &post.Created, &post.User.ID, &post.User.Name, &post.User.Created); err != nil {
			panic(err)
		}
		posts = append(posts, post)
	}

	if err = rows.Err(); err != nil {
		panic(err)
	}

	for _, v := range posts {
		fmt.Printf("作者姓名:%q\n", v.User.Name)
	}
}

希望对你有帮助。

编辑

我还包括了一个在Postgres中进行INSERT的示例。为了实现这个目标,我们需要使用db.Exec()函数,并提供参数。

注意如何构建查询,以避免SQL注入漏洞。

最后,在实际应用中,你不应该查找posts表中的最大id,而应该自动生成。

英文:

This approach is without any ORM.
It's a simple query that can return multiple rows. You've to scan the whole resultset and map each column on the struct's fields.
> Keep in mind to always check for errors.

Below you can find the solution:

package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/lib/pq"
)

type Post struct {
	ID        int
	PostTitle string
	PostDesc  string
	Created   time.Time
	UserID    int
	User      User
}

type User struct {
	ID      int
	Name    string
	Created time.Time
}

func main() {
	conn, err := sql.Open("postgres", "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable")
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// get MAX id
	var id int
	conn.QueryRow(`SELECT MAX(id) FROM posts`).Scan(&id)

	// insert
	sqlInsertStmt := `INSERT INTO posts (id, post_title, post_desc, created, user_id) VALUES ($1,$2,$3,$4,$5)`
	if _, err = conn.Exec(sqlInsertStmt, id+1, "TDD", "Introduction to Test-Driven-Development", time.Now(), 1); err != nil {
		panic(err)
	}

	// read
	rows, err := conn.Query(`SELECT posts.id, post_title, post_desc, posts.created, users.id, users.name, users.created FROM posts INNER JOIN users ON posts.user_id=users.id`)
	if err != nil {
		panic(err)
	}

	var posts []Post
	for rows.Next() {
		var post Post
		if err = rows.Scan(&post.ID, &post.PostTitle, &post.PostDesc, &post.Created, &post.User.ID, &post.User.Name, &post.User.Created); err != nil {
			panic(err)
		}
		posts = append(posts, post)
	}

	if err = rows.Err(); err != nil {
		panic(err)
	}

	for _, v := range posts {
		fmt.Printf("author name: %q\n", v.User.Name)
	}
}

Let me know if this helps.

Edit

I've also included an example of INSERT in Postgres. To achieve it, we've to use the db.Exec() function, and provide the parameters.
> Pay attention to how you construct the query as you can get a SQL-Injection vulnerability.

Lastly, in a real-world scenario, you shouldn't lookup for the MAX id in the posts table but should be automatically generated.

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

发表评论

匿名网友

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

确定