Go gorm check a model is associated with another model - go

I have a race model
type Race struct {
gorm.Model
Title string
Date string
Token string
Heats []Heat `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Runners []Runner `gorm:"many2many:race_runners;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
I want to add runners into heats but before doing so I want to assert that the runner actually is in the race
var race models.Race
if err := models.DB.Preload("Runners").Find(&race, "runners.id IN ?", []uint{runner.ID}).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Runner not in race!"})
return
}
I'm getting the following error
near "?": syntax error
Because I'm having a many to many relationship between Race and Runners I doubt that I can just do runners.id in the condition I pass to my Find method. But I'm not sure how to actually do what I want to achieve. Which is ensure the runner is already in the race before adding it to a heat. Any suggestions?

The where query wants to be in the Preload part. Then you can check the len(fetchedRace.Runners) < 1, and if that is true, the runner is not in that race.
fetchedRace := Race{}
db.Preload("Runners", "runners.id = ?", runnerOne.ID).Find(&fetchedRace, race.ID)
Full working example
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Race struct {
gorm.Model
Title string
Date string
Token string
Runners []Runner `gorm:"many2many:race_runners;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type Runner struct {
gorm.Model
Name string
}
func main() {
db, err := gorm.Open(sqlite.Open("many2many.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
_ = db.AutoMigrate(&Race{}, &Runner{})
raceOne := Race{
Title: "Race One",
}
db.Create(&raceOne)
runnerOne := Runner{
Name: "Runner One",
}
runnerTwo := Runner{
Name: "Runner Two",
}
db.Create(&runnerOne)
db.Create(&runnerTwo)
// Associate runners with race
err = db.Model(&raceOne).Association("Runners").Append([]Runner{runnerOne, runnerTwo})
// Fetch from DB
fetchedRace := Race{}
db.Debug().Preload("Runners", "runners.id = ?", runnerOne.ID).Find(&fetchedRace, raceOne.ID)
if len(fetchedRace.Runners) < 1 {
fmt.Println("error, runner not in race")
}
db.Delete(&raceOne)
db.Delete(&runnerOne)
db.Delete(&runnerTwo)
}
Using a custom SQL statement
This can be done in one SQL statement and gorm isn't very optimal with it's preloading, so if you are planning on using this often I would probably do something like this:
func isRunnerInRace(db *gorm.DB, runnerId uint, raceId uint) bool {
count := 0
db.Raw("SELECT COUNT(runner_id) FROM race_runners WHERE race_id = ? AND runner_id = ?", raceId,
runnerId).Scan(&count)
return count > 0
}

Related

Get data from two different struct

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
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.
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!

How to nest query child's child table data

I have a many to many table called users -> tasks, I can query tasks out with this:
var user models.User
DB.Find(&user, "user_addr=?", userAddr)
//log.Infof("find user: ", user)
DB.Model(&user).Related(&tasks, "Tasks")
But, in my tasks, I have a child table with is one to many, called subtasks. How do I query it out as a feild in tasks. Here is my tasks structure:
type Tasks struct {
gorm.Model
TasksID int `json:"tasks_id"`
// TasksCreateTime is the hash value since it is unique
//TasksHash string `json:"tasks_hash"`
// tasks related fields
TasksTitle string `json:"tasks_title"`
TasksCreateTime time.Time `json:"tasks_create_time"`
TasksComment string `json:"tasks_comment"`
SubTasks []SubTask `json:"sub_tasks" gorm:"foreignkey:ID"`
}
I want query the data out and assign it to SubTasks field.
How to do it nested?
If this way works or not?
func FetchSyncTasksForUser(userAddr string, tasks *[]models.Tasks) {
log.Infof("fetch sync tasks for user %s ", userAddr)
var user models.User
DB.Find(&user, "user_addr=?", userAddr)
DB.Preload("Tasks").Preload("Tasks.SubTasks").Find(&user, "user_addr = ?", userAddr)
tasks = &user.Tasks
}
Have you tried the gorm Preload feature? I put together this code snippet which preloads the necessary models. It will print out the formatted models to the command line.
Update: Includes a snippet to find a user by their ID https://gorm.io/docs/query.html
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Tasks []Task
}
type Task struct {
gorm.Model
UserId uint
SubTasks []SubTask
}
type SubTask struct {
gorm.Model
TaskId uint
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&User{}, &Task{}, &SubTask{})
// Create
user := User{Name: "C"}
db.Create(&user)
task := Task{UserId: user.ID}
db.Create(&task)
subTaskOne := SubTask{TaskId: task.ID}
subTaskTwo := SubTask{TaskId: task.ID}
db.Create(&subTaskOne)
db.Create(&subTaskTwo)
// Find all users, with tasks and subtasks
users := make([]User, 1)
db.Preload("Tasks").Preload("Tasks.SubTasks").Find(&users)
for _, u := range users {
jsonOutput, err := json.MarshalIndent(u, "", " ")
if err != nil {
panic("")
}
fmt.Println(string(jsonOutput))
}
// Find only one user by user_addr with tasks and subtasks
oneUser := User{}
db.Where("user_addr = ?", user.UserAddr).Preload("Tasks").Preload("Tasks.SubTasks").First(&oneUser)
jsonOutput, err := json.MarshalIndent(oneUser, "", " ")
if err != nil {
panic("")
}
fmt.Println(string(jsonOutput))
// Clean up
db.Delete(&subTaskTwo)
db.Delete(&subTaskOne)
db.Delete(&task)
db.Delete(&user)
}

How to call gorm alias properly?

Here is my code:
package main
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type ClientCustomer struct {
Id int `json:"Id"`
Name string
Created time.Time
key string
UserId int `gorm:"user_id"`
Modified time.Time
}
func (ClientCustomer) TableName() string {
return "Client_customer"
}
type ClientCustomerInvitation struct {
Id int
CustomerId int `gorm:"customer_id"`
CodeInvitationId int `gorm:"codeinvitation_id"`
}
func (ClientCustomerInvitation) TableName() string {
return "Client_customer_invitation"
}
func main() {
db, err := gorm.Open("sqlite3", "db.sqlite3?cache=shared&mode=rwc")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
var clientCustomer ClientCustomer
rows, err := db.Model(&ClientCustomer{}).Rows()
defer rows.Close()
if err != nil {
panic(err)
}
var clientCustomerInvitation ClientCustomerInvitation
for rows.Next() {
db.ScanRows(rows, &clientCustomer)
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
fmt.Println(clientCustomer)
fmt.Println(clientCustomerInvitation)
}
}
but I'm not fond of this line:
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
Is there a way to call "customer_id" from the struct directly instead of using a string?
Ideally I would like to do something like:
db.First(&clientCustomerInvitation, ClientCustomerInvitation.CustomerId.gormAlias+" = ?", clientCustomer.Id)
I'm looking for a way to use the gorm alias for mapping the field in way that is more elegant and re usable than a mere string.
The only way to be able to get tag value from certain struct field, is by using reflect.
My suggestion, create a function that return tag value from specific struct field. Something like below:
func getGormAlias(obj interface{}, fieldName string) string {
if field, ok := reflect.TypeOf(obj).FieldByName(fieldName); ok {
return field.Tag.Get("gorm")
}
return ""
}
Then use it to get the tag value.
gormAliasCustomerId := getGormAlias(ClientCustomerInvitation{}, "CustomerId")
db.First(&clientCustomerInvitation, gormAliasCustomerId + " = ?", clientCustomer.Id)
Basically what getGormAlias() function does:
Use the reflect.Type on obj to get the reflect.Type value.
Then call .FieldByName() to get the reflect.Value object from selected field name.
The tag information is available through .Tag property. Use that to get the tag value of gorm.

How can I make this object mapping more dry and reusable in Go?

I have created an object mapping in Go that is not relational, it is very simple.
I have several structs that looks like this:
type Message struct {
Id int64
Message string
ReplyTo sql.NullInt64 `db:"reply_to"`
FromId int64 `db:"from_id"`
ToId int64 `db:"to_id"`
IsActive bool `db:"is_active"`
SentTime int64 `db:"sent_time"`
IsViewed bool `db:"is_viewed"`
Method string `db:"-"`
AppendTo int64 `db:"-"`
}
To create a new message I just run this function:
func New() *Message {
return &Message{
IsActive: true,
SentTime: time.Now().Unix(),
Method: "new",
}
}
And then I have a message_crud.go file for this struct that looks like this:
To find a message by a unique column (for example by id) I run this function:
func ByUnique(column string, value interface{}) (*Message, error) {
query := fmt.Sprintf(`
SELECT *
FROM message
WHERE %s = ?
LIMIT 1;
`, column)
message := &Message{}
err := sql.DB.QueryRowx(query, value).StructScan(message)
if err != nil {
return nil, err
}
return message, nil
}
And to save a message (insert or update in the database) I run this method:
func (this *Message) save() error {
s := ""
if this.Id == 0 {
s = "INSERT INTO message SET %s;"
} else {
s = "UPDATE message SET %s WHERE id=:id;"
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(this))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(*this)
if err != nil {
return err
}
if this.Id == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
this.Id = lastId
}
return nil
}
The sql.PlaceholderPairs() function looks like this:
func PlaceholderPairs(i interface{}) string {
s := ""
val := reflect.ValueOf(i).Elem()
count := val.NumField()
for i := 0; i < count; i++ {
typeField := val.Type().Field(i)
tag := typeField.Tag
fname := strings.ToLower(typeField.Name)
if fname == "id" {
continue
}
if t := tag.Get("db"); t == "-" {
continue
} else if t != "" {
s += t + "=:" + t
} else {
s += fname + "=:" + fname
}
s += ", "
}
s = s[:len(s)-2]
return s
}
But every time I create a new struct, for example a User struct I have to copy paste the "crud section" above and create a user_crud.go file and replace the words "Message" with "User", and the words "message" with "user". I repeat alot of code and it is not very dry. Is there something I could do to not repeat this code for things I would reuse? I always have a save() method, and always have a function ByUnique() where I can return a struct and search by a unique column.
In PHP this was easy because PHP is not statically typed.
Is this possible to do in Go?
Your ByUnique is almost generic already. Just pull out the piece that varies (the table and destination):
func ByUnique(table string, column string, value interface{}, dest interface{}) error {
query := fmt.Sprintf(`
SELECT *
FROM %s
WHERE %s = ?
LIMIT 1;
`, table, column)
return sql.DB.QueryRowx(query, value).StructScan(dest)
}
func ByUniqueMessage(column string, value interface{}) (*Message, error) {
message := &Message{}
if err := ByUnique("message", column, value, &message); err != nil {
return nil, err
}
return message, error
}
Your save is very similar. You just need to make a generic save function along the lines of:
func Save(table string, identifier int64, source interface{}) { ... }
Then inside of (*Message)save, you'd just call the general Save() function. Looks pretty straightforward.
Side notes: do not use this as the name of the object inside a method. See the link from #OneOfOne for more on that. And do not get obsessed about DRY. It is not a goal in itself. Go focuses on code being simple, clear, and reliable. Do not create something complicated and fragile just to avoid typing a simple line of error handling. This doesn't mean that you shouldn't extract duplicated code. It just means that in Go it is usually better to repeat simple code a little bit rather than create complicated code to avoid it.
EDIT: If you want to implement Save using an interface, that's no problem. Just create an Identifier interface.
type Ider interface {
Id() int64
SetId(newId int64)
}
func (msg *Message) Id() int64 {
return msg.Id
}
func (msg *Message) SetId(newId int64) {
msg.Id = newId
}
func Save(table string, source Ider) error {
s := ""
if source.Id() == 0 {
s = fmt.Sprintf("INSERT INTO %s SET %%s;", table)
} else {
s = fmt.Sprintf("UPDATE %s SET %%s WHERE id=:id;", table)
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(source))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(source)
if err != nil {
return err
}
if source.Id() == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
source.SetId(lastId)
}
return nil
}
func (msg *Message) save() error {
return Save("message", msg)
}
The one piece that might blow up with this is the call to Exec. I don't know what package you're using, and it's possible that Exec won't work correctly if you pass it an interface rather than the actual struct, but it probably will work. That said, I'd probably just pass the identifier rather than adding this overhead.
You probably want to use an ORM.
They eliminate a lot of the boilerplate code you're describing.
See this question for "What is an ORM?"
Here is a list of ORMs for go: https://github.com/avelino/awesome-go#orm
I have never used one myself, so I can't recommend any. The main reason is that an ORM takes a lot of control from the developer and introduces a non-negligible performance overhead. You need to see for yourself if they fit your use-case and/or if you are comfortable with the "magic" that's going on in those libraries.
I don't recommend doing this, i personally would prefer being explicit about scanning into structs and creating queries.
But if you really want to stick to reflection you could do:
func ByUnique(obj interface{}, column string, value interface{}) error {
// ...
return sql.DB.QueryRowx(query, value).StructScan(obj)
}
// Call with
message := &Message{}
ByUnique(message, ...)
And for your save:
type Identifiable interface {
Id() int64
}
// Implement Identifiable for message, etc.
func Save(obj Identifiable) error {
// ...
}
// Call with
Save(message)
The approach i use and would recommend to you:
type Redirect struct {
ID string
URL string
CreatedAt time.Time
}
func FindByID(db *sql.DB, id string) (*Redirect, error) {
var redirect Redirect
err := db.QueryRow(
`SELECT "id", "url", "created_at" FROM "redirect" WHERE "id" = $1`, id).
Scan(&redirect.ID, &redirect.URL, &redirect.CreatedAt)
switch {
case err == sql.ErrNoRows:
return nil, nil
case err != nil:
return nil, err
}
return &redirect, nil
}
func Save(db *sql.DB, redirect *Redirect) error {
redirect.CreatedAt = time.Now()
_, err := db.Exec(
`INSERT INTO "redirect" ("id", "url", "created_at") VALUES ($1, $2, $3)`,
redirect.ID, redirect.URL, redirect.CreatedAt)
return err
}
This has the advantage of using the type system and mapping only things it should actually map.

golang working effectively with Null* types

If one is working with a database, a Null* type is useful for most scenarios as one typically does not want a "zero" value going through, you want the NOT NULL constraints etc. to kick up and remind you that you haven't passed in all the data necessary.
So you create a structure like the following:
type Role struct {
Id sql.NullInt64
Code sql.NullString
}
Thats great, but now you cannot get direct access to the properties and have to use Role.Id.Value to both get and set, this is going to get pretty old in a large app when you have to have the extra step every single time you want access to the properties.
It would be nice if you could assign directly eg. Role.Code = "Fsfs", and be able to do something like Role.Code.IsNull when you are interested in null checking. Is such a thing possible?
Is using intermediate pointer value(s) an option?
package main
import "fmt"
type tmp struct {
Value int
}
func getInt() *int {
i := 123
return &i
}
func main() {
// Re
var v *int
v = nil
fmt.Printf("%T / %v\n", v, v)
if v == nil {
println("nil...")
}
v = getInt()
fmt.Printf("%T / %v\n", v, *v)
if v != nil {
println("not nil...")
}
s := tmp{*v}
fmt.Printf("%T / %v\n", s, s)
}
http://play.golang.org/p/lBrwTKh6-v
You can access Role.Code like that:
var r *Role
r.Code = *code
You can check for null like this:
fmt.Println(r.Code, r.Code.Valid)
If you change the value of r.Code manually without using an sql.Scanner a Setter could be helpful:
func (r *Role) SetCode(code string) {
r.Code.String = code
r.Code.Valid = true
}
func main() {
var r *Role
r.SetCode("mi")
if r.Code.Valid {
fmt.Println(r.Code)
}
I tried this out here: https://play.golang.org/p/faxQUm-2lr
Keep app and database code separate.
// Role belongs to app code, no compromises.
type Role struct {
Id int64
Code string
}
Model the database.
// Database has tables with columns.
type Table struct {
Name string
Columns []string
}
var RoleTable = Table{
Name: "roles",
Columns: []string{
"id",
"code",
},
}
Write code once to convert between model and database row.
// Database package needs to make it work.
// Write a model to database row.
type Writer struct {
Role
}
func (w *Writer) Write() []interface{} {
return []interface{}{
w.Role.Id,
sql.NullString{
Valid: len(w.Role.Code) > 0,
String: w.Role.String,
},
}
}
// Read a database row into model.
type Reader struct {
Id int64
Code sql.NullString
}
func (r *Reader) Scan(row *sql.Row) error {
return row.Scan(
&r.Id,
&r.Code,
)
}
func (r *Reader) Read() Role {
return Role{
Id: r.Id,
Code: r.Code.String,
}
}
Your schema is decoupled from app model. You can flatten structures like user contact details when saving or loading.
// Nested struct in app code.
type User struct {
TwitterProfile struct {
Id string
ScreenName string
}
}
// Database row is normalized flat.
var UserTable = Table{
Name: "users",
Columns: []string{
"twitter_id",
"twitter_screen_name",
},
}
It's flexible. You can even scan join rows without intermediate structs.
type RowMux struct {
vs []interface{}
}
func (mux *RowMux) Scan(vs ...interface{}) error {
mux.vs = append(mux.vs, vs...)
return nil
}
func (mux *RowMux) Mux(row *sql.Row) error {
return row.Scan(mux.vs...)
}
// Scan join rows!
row := db.QueryRow(`
SELECT users.*, roles.*
FROM users
JOIN roles ON users.id = roles.user_id
WHERE users.twitter_id = "123"
`)
mux := &RowMux{}
userReader := &UserReader{}
userReader.Scan(mux)
roleReader := &RoleReader{}
roleReader.Scan(mux)
if err := mux.Mux(row); err != nil {
panic(err)
}
user := userReader.Read()
role := roleReader.Read()

Resources