LoadRelated of a list in beego - go

I am wondering how the correct approach is to load related fields in beego.
The doc explains it like this:
type User struct {
Id int
Name string
Posts []*Post `orm:"reverse(many)"`
}
user := User{Id: 1}
err := dORM.Read(&user)
num, err := dORM.LoadRelated(&user, "Posts")
This makes sense as long as I only query one record. What is the correct way to fetch related fields when I query all users?
A possible solution would be like this:
var users []*User
o.QueryTable(new(User)).All(&users)
for _, user := range users {
o.LoadRelated(controlCategory, "Posts")
}
However, this means I have to loop everytime over the complete list and make for every record a DB query to load all records.
Any suggestions? Thanks!

Related

Why isn't the variable overwritten?

If you write it this way
func showAll(db *gorm.DB) {
users := &[]models.User{}
card := models.Card{}
db.Find(users)
for _, i := range *users {
fmt.Println(i)
db.Where("user_id = ?", i.ID).Find(&card)
fmt.Println(card)
}
}
then fmt.Println(card) always prints the first value.
But if you write it this way
func showAll(db *gorm.DB) {
users := &[]models.User{}
db.Find(users)
for _, i := range *users {
fmt.Println(i)
card := models.Card{}
db.Where("user_id = ?", i.ID).Find(&card)
fmt.Println(card)
}
}
It prints correctly. Why? Shouldn't the &card variable be overwritten?
I wanted to print all the cards for found users.
Your first problem is that you aren't handling errors, if you were then you would know that all queries after the first one result in record-not-found. Second of your problems is the fact that you don't seem to be aware that gorm has debugging which, if you turn it on, will show the generated SQL and if you were to look at that you would immediately spot what's wrong. Finally, your actual problem, is the fact that after the first iteration the card struct instance has a non-zero ID which, alongside the explicit i.ID is also used in the WHERE clause.
So unless i.ID and card.ID are identical (or card.ID is zero) then:
db.Where("user_id = ?", i.ID).Find(&card).Error == ErrRecordNotFound
https://gorm.io/docs/query.html#String-Conditions
If the object’s primary key has been set, then condition query wouldn’t cover the value of primary key but use it as a ‘and’ condition. For example:
var user = User{ID: 10}
db.Where("id = ?", 20).First(&user)
// SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1
This query would give record not found Error. So set the primary key attribute such as id to nil before you want to use the variable such as user to get new value from database.

How to use Go / GORM to print SELECT query output without pre-defined struct

I am developing an API using Go which connects to MySQL database for some query execution. Am using GORM for database operations. But am stuck at printing the SELECT query output for the tables which I don't have the column names.
My use case is that, I need to run the query on multiple tables where I don't have an idea about what their column names and types are. And so I cannot pre-define a struct for all the current and future tables which might get added.
Is there a way to print/save the SELECT query output without a pre-defined struct ?
I tried do some using empty struct but it didn't help me.
P.S: Am a beginner in Go
type Testing struct{}
var test Testing
dsn := fmt.Sprintf("%v:%v#tcp(%v:%v)/%v", myds.DBuser, myds.DBpassword, myds.DBhost, myds.DBport, myds.DBname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
tx := db.Raw(query).Scan(&test)
if tx.Error != nil {
fmt.Println(tx.Error)
}
fmt.Println(test)
You can use an anonymous struct
Let's say you have a struct:
type User struct{
FirstName string
LastName string
}
Query:
SELECT CONCAT(first_name,last_name) AS full_name from users;
Notice the new column full_name
you can simply do
var fullName = struct{FullName string}{}
Notice how I use pascal case & FullName has to be the field name
A capital letter in between will represent a _
Field is public so it can be accessed outside.
full_name(query) = FullName(field)
pass this fullName object as a bucket to your Scan and it should work.
db.Raw(query).Scan(&fullName)
EDIT:
Your query will have some result right?
Let me assume that you have
column_one,column_two... column_n
Now, to get the data from all the columns or selected ones if you want, you simply have to define fields (in anonymous struct) with specific names. In our case:
struct{ColumnOne,ColumnTwo,..ColumnN interface{}}{}
P.S. I have used interface{}, you can use types depending on the data your column returns.
It worked for me by using a map type with interface. This helped me to save the SELECT query results without pre-defined struct or the column names.
dsn := fmt.Sprintf("%v:%v#tcp(%v:%v)/%v", myds.DBuser, myds.DBpassword, myds.DBhost, myds.DBport, myds.DBname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
var result []map[string]interface{}
tx := db.Raw(query).Scan(&result)
if tx.Error != nil {
fmt.Println(tx.Error)
return
}
bytes, _ := json.Marshal(result)
fmt.Println(string(bytes))

How to fetch last record in gorm?

I'm working on a golang application in which I need to fetch last record from the table so I'm not able to do it. I have models mention below:-
type SQLTransaction struct {
Id int `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty" gorm:"primaryKey"`
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"`
VmStatusId int `protobuf:"bytes,6,opt,name=vm_status_id,proto3" json:"vm_status_id,omitempty"`
}
Here is my gorm function in which I want to fetch the last record
func (mgr *manager) GetLastTxn() (int, error) {
var versionId int
resp := mgr.connection.Table("transactions").Last("", "version")
return versionId, resp.Error
}
Error:-
model value required
[0.053ms] [rows:0] SELECT * FROM `transactions` WHERE version ORDER BY `transactions`. DESC LIMIT 1
0 model value required
How can I achieve it please any help. Thanks in advance.
There are a couple of issues in your example that should be handled.
First, the error model value required suggests that the Last method needs a model to store the result. If you want the entire record with all the data you can do it like this:
var lastVersion SQLTransaction
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you only want the ID of the record, you do something like this:
var lastVersion struct {
ID int
}
resp := mgr.connection.Table("transactions").Last(&lastVersion)
Next, you don't need Last("", "version") because it doesn't do what you think it does. The first parameter is a pointer to your resulting object (which for all go-gorm methods always should be an object or a slice). The second one is a condition for the WHERE clause. So the correct syntax, if you don't need an additional WHERE clause, is:
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you look at the code of the Last method, it considers the primary key when it executes the method, so you don't even need any additional parameters or conditions.

Query result is memory address

I'm new to go and still confused about pointers but I have followed the instructions for querying multiple rows but the result I get back is series of memory addresses instead of actual values.
This same structure, minus the rows.Next() works just fine for a single user so I'm confused as to the origin of the problem here.
Ultimately I'm trying to use the results of the function in a template but I'm trying to figure out the structure of it so I can range it in my HTML.
For example, if I try to run the code below, I get something like: &{[0xc... 0xc... 0xc...]}
type User struct {
Id int `json:"int"`
Name string `json:"name"`
Role string `json:"role"`
}
type Users struct {
Users []*User
}
func getUsers(company string) *Users {
users := Users{}
rows, err := db.Query("SELECT Id, Name, Role...")
// Check err
defer rows.Close()
for rows.Next() {
user := &User{}
err = rows.Scan(&user.Id, &user.Name, &user.Role)
// Check err
users.Users = append(users.Users, user)
}
err = rows.Err()
// Check err
return &users
}
This is how I'm attempting to use the function
func userView(w http.ResponseWriter, r *http.Request) {
res := getUsers("test") // Should return 3 results
fmt.Println(res.Users)
}
The problem isn't in your fetching of the data, it's in your display of the data. fmt.Println() prints memory addresses when given pointers--so it's behaving exactly as expected.
If you instead do:
fmt.Printf("%+v", res.Users)
you'll get a different result, probably closer to what you expect.
If you're planning to use a template, then you should do so--your template should be able to access the fields of each User just fine.
But the short answer is: Your testing method is invalid.
Type Users is a slice of pointers. If you print the return value of getUsers it looks like a bunch of memory addresses. This is OK.
If you want to print something more meaningful, write a String() method for Users in which you dereference each pointer and build a string containing struct fields.

Iterate over struct and perform database queries

So I'm new to go and I come from a javascript/node background and for practice, I've been rewriting some of my javascript code into go.
I have a situation where I have an struct (in node it was my object) and I need to iterate over it and perform two database queries. I have something that works but it seems costly and repetitive.
Struct:
type SiteUsers struct {
Active struct {
Moderators []string `json:"moderators"`
Admins []string `json:"admins"`
Regulars []string `json:"regulars"`
} `json:"active"`
}
Then in the function where I handle an api request that returns JSON binded to this struct I use a for range loop for each role under active. For each one I perform the same first query and then a second one that is specific to each one.
v := getSiteUsers(&usrs, website)
for _, moderators := range v.Active.Moderators {
// Insert into user table
// Insert into user table with role of moderator
}
for _, admins := range v.Active.Admins {
// Insert into user table
// Insert into user table with role of admin
}
for _, regulars := range v.Active.Regulars {
// Insert into user table
// Insert into user table with role of regular
}
This method will work but it doesn't feel completely right and I would love to get some input from people experienced with go.
Would something like this be better?
v := getSiteUsers(&usrs, website)
insertUsers := func(users []string, role roleType) {
for _, user := range users {
// Insert into user table
// Insert into user table with given role
}
}
insertUsers(v.Active.Moderators, moderatorRole)
insertUsers(v.Active.Admins, adminRole)
insertUsers(v.Active.Regulars, regularRole)

Resources