Everything worked, but when I cleared the database and started the application again, I get this error: ERROR: relation "orders" does not exist (SQLSTATE 42P01)
My code:
type Cart struct {
gorm.Model
Products JSONB `gorm:"type:jsonb" json:"products"`
OrderID uint
}
type Jurik struct {
gorm.Model
Inn string `json:"inn" gorm:column:"inn"`
...
OrderID uint
}
type Phyz struct {
gorm.Model
Name string `json:"name" gorm:column:"name"`
...
OrderID uint
}
type Order struct {
gorm.Model
Cart Cart `json:"cart"`
User_id string `json:"user_id"`
Jurik Jurik `json:"jurik"`
Phyz Phyz `json:"phyz"`
}
I really don't understand what could be wrong, because my Cart Jurik Phyz tables are related to Order
I don't know how it works, but my code before looked like this:
func Connect() {
db, err := gorm.Open(postgres.Open("postgres://postgres:qwerty#localhost:5432/shop"), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&models.Categories{}, &models.Products{}, &models.Pagination{}, &models.Feedback{}, &models.Cart{}, &models.Jurik{}, &models.Phyz{}, &models.Order{})
DB = db
}
Then I tried to make a separate migration for Order:
func Connect() {
db, err := gorm.Open(postgres.Open("postgres://postgres:qwerty#localhost:5432/shop"), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&models.Categories{}, &models.Products{}, &models.Pagination{}, &models.Feedback{}, &models.Cart{}, &models.Jurik{}, &models.Phyz{})
db.AutoMigrate(&models.Order{})
DB = db
}
Hope this helps someone!
Related
Model Account contains nested structures - Currency and User
When I create a new instance of Account in DB, and then return it in my response, nested entities are empty:
type Account struct {
BaseModel
Name string `gorm:"size:64;not null" json:"name"`
Balance decimal.Decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"`
UserID int `gorm:"not null" json:"-"`
User User `gorm:"foreignKey:UserID" json:"user"`
CurrencyID int `gorm:"not null" json:"-"`
Currency Currency `gorm:"foreignKey:CurrencyID" json:"currency"`
}
type CreateAccountBody struct {
Name string `json:"name" binding:"required"`
Balance decimal.Decimal `json:"balance"`
CurrencyID int `json:"currency_id" binding:"required"`
}
func CreateAccount(ctx *gin.Context) {
body := CreateAccountBody{}
if err := ctx.Bind(&body); err != nil {
log.Println("Error while binding body:", err)
ctx.JSON(
http.StatusBadRequest,
gin.H{"error": "Wrong request parameters"},
)
return
}
account := Account {
Name: body.Name,
Balance: body.Balance,
CurrencyID: body.CurrencyID,
UserID: 1,
}
if result := db.DB.Create(&account); result.Error != nil {
log.Println("Unable to create an account:", result.Error)
}
ctx.JSON(http.StatusCreated, gin.H{"data": account})
}
To avoid this problem, I refresh account variable with separate query:
db.DB.Create(&account)
db.DB.Preload("User").Preload("Currency").Find(&account, account.ID)
ctx.JSON(http.StatusCreated, gin.H{"data": account})
Is this the most effective and correct way to achieve the desired result?
I'm gonna share you how usually I managed this scenario. First, let me share the code.
main.go file
package main
import (
"context"
"gogindemo/handlers"
"github.com/gin-gonic/gin"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var (
db *gorm.DB
ctx *gin.Context
)
func init() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
var err error
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&handlers.Currency{})
db.AutoMigrate(&handlers.User{})
db.AutoMigrate(&handlers.Account{})
}
func AddDb() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Request = ctx.Request.WithContext(context.WithValue(ctx.Request.Context(), "DB", db))
ctx.Next()
}
}
func main() {
db.Create(&handlers.User{Id: 1, Name: "john doe"})
db.Create(&handlers.User{Id: 2, Name: "mary hut"})
db.Create(&handlers.Currency{Id: 1, Name: "EUR"})
db.Create(&handlers.Currency{Id: 2, Name: "USD"})
r := gin.Default()
r.POST("/account", AddDb(), handlers.CreateAccount)
r.Run()
}
Here, I've just added the code for bootstrapping the database objects and add some dummy data to it.
handlers/handlers.go file
package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
type User struct {
Id int
Name string
}
type Currency struct {
Id int
Name string
}
type Account struct {
Id int
Name string `gorm:"size:64;not null" json:"name"`
Balance decimal.Decimal `gorm:"type:decimal(16, 2);default:0;not null;" json:"balance"`
UserID int `gorm:"not null" json:"-"`
User User `gorm:"foreignKey:UserID" json:"user"`
CurrencyID int `gorm:"not null" json:"-"`
Currency Currency `gorm:"foreignKey:CurrencyID" json:"currency"`
}
type CreateAccountBody struct {
Name string `json:"name" binding:"required"`
Balance decimal.Decimal `json:"balance"`
CurrencyID int `json:"currency_id" binding:"required"`
}
func CreateAccount(c *gin.Context) {
db, ok := c.Request.Context().Value("DB").(*gorm.DB)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
var accountReq CreateAccountBody
if err := c.BindJSON(&accountReq); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "wrong request body payload"})
return
}
// create Account & update the "account" variable
account := Account{Name: accountReq.Name, Balance: accountReq.Balance, CurrencyID: accountReq.CurrencyID, UserID: 1}
db.Create(&account).Preload("Currency").Preload("User").Find(&account, account.Id)
c.IndentedJSON(http.StatusCreated, account)
}
Within this file, I actually talk with the database through the DB passed in the context. Now, back to your question.
If the relationship between the Currency/Account and User/Account is of type 1:1, then, you should rely on the Preload clause. This will load the related entity in a separate query instead of adding it in an INNER JOIN clause.
Let me know if this solves your issue, thanks!
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!
I have two structs like this:
type User struct {
gorm.Model
...
Newsletters []Newsletter `gorm:"many2many:user_newsletter"`
}
type Newsletter struct {
gorm.Model
...
Users []User `gorm:"many2many:user_newsletter"`
}
However when I try to add a new Newletter to an User no record is created in the table user_newsletter. This is my current code:
func (u *User) SaveUserNewsletter(n *Newsletter) (*User, error) {
DB.Model(&u).Association("Newsletters")
DB.Model(&u).Association("Newsletters").Append(&n)
var err = DB.Session(&gorm.Session{FullSaveAssociations: true}).Omit("password").Save(&u).Error
if err != nil {
return &User{}, err
}
return u, nil
}
No errors are showing, but no records are created in my "joined" table.
Thank you
I'm trying to set up an association between Users and PredictionsBags. My problem is that everything works OK if I use GORM's assumed names for referring objects, but I'd like to change the names a bit.
type User struct {
gorm.Model
// We’ll try not using usernames for now
Email string `gorm:"not null;unique_index"`
Password string `gorm:"-"`
PasswordHash string `gorm:"not null"`
Remember string `gorm:"-"` // A user’s remember token.
RememberHash string `gorm:"not null;unique_index"`
Bags []PredictionsBag
}
Every user, of course, owns zero or more PredictionsBags:
type PredictionsBag struct {
gorm.Model
UserID uint // I want this to be "OwnerID"
Title string
NotesPublic string `gorm:"not null"` // Markdown field. May be published.
NotesPrivate string `gorm:"not null"` // Markdown field. Only for (private) viewing and export.
Predictions []Prediction
}
And I'd like to have .Related() work in the usual way:
func (ug *userGorm) ByEmail(email string) (*User, error) {
var ret User
matchingEmail := ug.db.Where("email = ?", email)
err := first(matchingEmail, &ret)
if err != nil {
return nil, err
}
var bags []PredictionsBag
if err := ug.db.Model(&ret).Related(&bags).Error; err != nil {
return nil, err
}
ret.Bags = bags
return &ret, nil
}
My problem is that I can't find a way to change PredictionsBag.UserID to anything else and still have GORM figure out the relationships involved. I've been reading http://gorm.io/docs/has_many.html#Foreign-Key and if I change the relevant lines to
type User struct {
// …
Bags []PredictionsBag `gorm:"foreignkey:OwnerID"`
}
and
type PredictionsBag struct {
// …
OwnerID uint
// …
}
I get this error:
[2019-07-28 14:23:49] invalid association []
What am I doing wrong? I've also been reading http://gorm.io/docs/belongs_to.html, but I'm not sure which page to follow more closely.
I'll have to Check Related() when I get home, but I think what you're looking for is Preload() This is my example that works with what you want.
package main
import (
"errors"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"log"
)
var DB *gorm.DB
func init() {
var err error
DB, err = gorm.Open("mysql", fmt.Sprintf("%s:%s#tcp(%s:3306)/%s?&parseTime=True&loc=Local", "root", "root", "localhost", "testing"))
if err != nil {
log.Fatal(err)
}
DB.DropTableIfExists(&User{}, &PredictionsBag{})
DB.AutoMigrate(&User{}, &PredictionsBag{})
user := User{Email:"dave#example.com"}
user.Bags = append(user.Bags, PredictionsBag{OwnerID: user.ID, NotesPrivate: "1", NotesPublic: "1"})
DB.Create(&user)
}
func main() {
user := User{Email:"dave#example.com"}
err := user.ByEmail()
if err != nil {
log.Println(err)
}
fmt.Println(user.ID, user.Email, "Bags:", len(user.Bags))
DB.Close()
}
type User struct {
gorm.Model
// We’ll try not using usernames for now
Email string `gorm:"not null;unique_index"`
Password string `gorm:"-"`
PasswordHash string `gorm:"not null"`
Remember string `gorm:"-"` // A user’s remember token.
RememberHash string `gorm:"not null;unique_index"`
Bags []PredictionsBag `gorm:"foreignkey:OwnerID"`
}
type PredictionsBag struct {
gorm.Model
OwnerID uint
Title string
NotesPublic string `gorm:"not null"` // Markdown field. May be published.
NotesPrivate string `gorm:"not null"` // Markdown field. Only for (private) viewing and export.
}
func (ug *User) ByEmail() error {
DB.Where("email = ?", ug.Email).Preload("Bags").Limit(1).Find(&ug)
if ug.ID == 0 {
return errors.New("no user found")
}
return nil
}
Using this might work with related, but I'm not sure what else needs to be changed:
Bags []PredictionsBag `gorm:"foreignkey:OwnerID;association_foreignkey:ID"`
Update:
I can get the Related() method to work, if you state the ForeignKey like the following:
DB.Where("email = ?", ug.Email).Limit(1).Find(&ug)
if ug.ID == 0 {
return errors.New("no user found")
}
if err := DB.Model(&ug).Related(&ug.Bags, "owner_id").Error; err != nil {
return err
}
I have struct called User:
type User struct {
Email string
Name string
}
and struct called UserDALModel:
type UserDALModel struct {
gorm.Model
models.User
}
gorm Model looks like this:
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
this is possible to make UserDALModel nested with gorm model and user model so the output will be:
{
ID
CreatedAt
UpdatedAt
DeletedAt
Email
Name
}
now the output is:
{
Model: {
ID
CreatedAt
UpdatedAt
DeletedAt
}
User: {
Name
Email
}
}
According to this test in gorm, I think you need to add an embedded tag to the struct.
type UserDALModel struct {
gorm.Model `gorm:"embedded"`
models.User `gorm:"embedded"`
}
You can also specify a prefix if you want with embedded_prefix.
I found the answer:
type UserModel struct {
Email string
Name string
}
type UserDALModel struct {
gorm.Model
*UserModal
}
------------------------------
user := UserModel{"name", "email#email.com"}
userDALModel := UserDALModel{}
userDal.UserModal = &user
be careful embedding two structs with the same column:
package tests
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"testing"
)
type A struct {
X string
Y string
}
type B struct {
X string
Y string
}
type AB struct {
B B `gorm:"embedded"` // Embedded struct B before struct A
A A `gorm:"embedded"`
}
var DB *gorm.DB
func connectDB() error {
var err error
spec := "slumberuser:password#tcp(localhost:3306)/slumber"
DB, err = gorm.Open("mysql", spec+"?parseTime=true&loc=UTC&charset=utf8")
DB.LogMode(true) // Print SQL statements
//defer DB.Close()
if err != nil {
return err
}
return nil
}
// cd tests
// go test -run TestGormEmbed
func TestGormEmbed(t *testing.T) {
if err := connectDB(); err != nil {
t.Errorf("error connecting to db %v", err)
}
values := []interface{}{&A{}, &B{}}
for _, value := range values {
DB.DropTable(value)
}
if err := DB.AutoMigrate(values...).Error; err != nil {
panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
}
DB.Save(&A{X: "AX1", Y: "AY1"})
DB.Save(&A{X: "AX2", Y: "AY2"})
DB.Save(&B{X: "BX1", Y: "BY1"})
DB.Save(&B{X: "BX2", Y: "BY2"})
//select * from `as` join `bs`;
// # x,y,x,y
// # AX1,AY1,BX1,BY1
// # AX2,AY2,BX1,BY1
// # AX1,AY1,BX2,BY2
// # AX2,AY2,BX2,BY2
var abs []AB
DB.Select("*").
Table("as").
Joins("join bs").
Find(&abs)
for _, ab := range abs {
fmt.Println(ab.A, ab.B)
}
// if it worked it should print
//{AX1 AY1} {BX1 BY1}
//{AX2 AY2} {BX1 BY1}
//{AX1 AY1} {BX2 BY2}
//{AX2 AY2} {BX2 BY2}
// but actually prints
//{BX1 BY1} {AX1 AY1}
//{BX1 BY1} {AX2 AY2}
//{BX2 BY2} {AX1 AY1}
//{BX2 BY2} {AX2 AY2}
}