I'm using gorm to handle database queries and I have 2 models (ManyToMany) :
type Person struct {
ID uint `json:"id" gorm:"primary_key;unique;autoIncrement"`
Name string `json:"name" binding:"required"`
Family string `json:"family" binding:"required"`
Companies []Company `json:"companies" gorm:"many2many:person_companies;"`
}
type Company struct {
ID uint `json:"id" gorm:"primary_key;unique;autoIncrement"`
Name string `json:"name"`
cars []Car
}
i use this query for receive list of my users:
func GetAllPeople() *[]domain.Person {
var people []domain.Person
db.Find(&people)
return &people
}
this works but shows me Null for companies
{
"id": 0,
"name": "erfan",
"family": "",
"companies": null
}
what should I use in query to show users companies (id) in a list?
You will have to use the Preload method with custom loading to just load the company ID's into the Companies field.
func GetAllPeople() *[]domain.Person {
var people []domain.Person
tx := db.Preload("Companies", func(db *gorm.DB) *gorm.DB {
return db.Select("ID")
}).Find(&people)
if tx.Error != nil {
// handle error
}
return &people
}
You can find more details on this link or this question.
Related
I'm really new to go and I decided to create a RestAPI for skill practising. So now I'm trying to make a one to many relationship between my User and Book entities. But when I try to save my Book entity to database, it shows me such error:
invalid field found for struct test/entity.UserEntity's field Books: define a valid foreign key for relations or implement the Valuer
/Scanner interface
Here's my code:
UserEntity
type UserEntity struct {
gorm.Model
Username string `gorm:"unique;not null"`
Email string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Books []BookEntity `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
func (*UserEntity) TableName() string {
return "users"
}
BookEntity
type BookEntity struct {
gorm.Model
Name string `gorm:"not null"`
Isbn int `gorm:"unique;not null"`
AuthorID uint
}
func (*BookEntity) TableName() string {
return "books"
}
BookAddModel
type BookAddModel struct {
Name string `json:"name"`
Isbn int `json:"isbn"`
}
func (bookModel *BookAddModel) ToModel(ctx *gin.Context) {
ctx.Bind(&bookModel)
}
Here's the code where I add entities to database
func AddBook(bookModel models.BookAddModel, author *entity.UserEntity) entity.BookEntity {
var book entity.BookEntity
mapstructure.Decode(bookModel, &book)
config.DbSession.Create(&book) // <<<< here's error
author.Books = append(author.Books, book)
config.DbSession.Save(author)
return book
}
Config
var DbSession *gorm.DB
var PostgresData = "host=localhost user=postgres password=4122 dbname=gorm port=5432 sslmode=disable TimeZone=Asia/Shanghai"
So what can bee the problem? If you know, please tell me. I'd really appreciate it!
You should set foreignKey for UserEntity.Books:
type UserEntity struct {
...
Books []BookEntity `gorm:"foreignKey:AuthorID,..."`
}
Just don't forget to drop tables first and rerun AutoMigrations:
db.AutoMigrate(&UserEntity{})
db.AutoMigrate(&BookEntity{})
I'm now having a problem with getting an id of array feeds from database (Postgres) with Gorm.
How can I query and return id array feeds? I don't know how to get only id from struct without loop
feeds := []models.Feed{}
feedID := []string{}
db.Select("id").Where("user_id = ?", "admin1").Find(&feeds)
for _, feed := range feeds {
feedID = append(feedID, feed.ID)
}
utils.PrintStruct(feeds)
This is feed model file:
type Feed struct {
Model
Status string `json:"status"`
PublishAt *time.Time `json:"publishAt"`
UserID string `json:"userID,omitempty"`
}
This is model base data model using for data entity:
type Model struct {
ID string `json:"id" gorm:"primary_key"`
}
Result:
[
{
"id": "d95d4be5-b53c-4c70-aa09",
"status": "",
"publishAt": null,
"userID":""
},
{
"id": "84b2d46f-a24d-4854-b44d",
"status": "",
"publishAt": null,
"userID":""
}
]
But I want like this:
["d95d4be5-b53c-4c70-aa09","84b2d46f-a24d-4854-b44d"]
You can use pluck
var ids []string
db.Model(&Feed{}).Where("user_id = ?", "admin1").Pluck("id", &ids)
There is a way to get all structs in package (entity in this case) to generate an automatic migrations list?
I split entities and migration package and now we have a package dedicated to all entities used by gorm and this is how I manage migration currently, completely manual to each new entity added we have to modify the migration main code adding the new entity to the migrationsList
package entity
type ClockOffset struct {
Model
ID string `json:"id" gorm:"primarykey"`
LastSyncClock uint32 `json:"last_sync_clock" gorm:"not null"`
LastSyncTimestamp uint32 `json:"last_sync_timestamp" gorm:"not null"`
}
type InflightMessage struct {
Model
ID string `json:"id" gorm:"primarykey"`
Token uint8 `gorm:"not null;uniqueIndex:idx_gateway_id_token"`
Tag string `gorm:"not null"`
}
// ... other entities ...
package main
func main() {
var migrationsList []*gormigrate.Migration
migrationsList = append(migrationsList, &gormigrate.Migration{
ID: "001-ClockOffset-CreateTable",
Migrate: func(tx repo.Database) error {
return tx.Migrator().CreateTable(&entity.ClockOffset{})
},
Rollback: func(tx repo.Database) error {
return tx.Migrator().DropTable(&entity.ClockOffset{})
},
})
migrationsList = append(migrationsList, &gormigrate.Migration{
ID: "002-InflightMessage-CreateTable",
Migrate: func(tx repo.Database) error {
return tx.Migrator().CreateTable(&entity.InflightMessage{})
},
Rollback: func(tx repo.Database) error {
return tx.Migrator().DropTable(&entity.InflightMessage{})
},
})
m := gormigrate.New(repo.Db(), gormigrate.DefaultOptions, migrationsList)
if err := m.Migrate(); err != nil {
panic(err)
}
}
I'd like to find an automatic way to read all the structures present on the package entity loop and append them to migrationsList
I'm building an api to learn more from Go and I'm having a problem with the many to many relationship between 2 entities.
I have 2 models
type Patient struct {
ID uint `json:"id" gorm:"primary_key"`
Name string `json: "name"`
Surname string `json: "surname"`
Cellphone int `json: "cellphone"`
Email string `json: "email"`
Professionals []*Professional `json: "professionals" gorm:"many2many:professional_patient"`
}
and
type Professional struct {
ID uint `json:"id" gorm:"primary_key"`
Name string `json:"name"`
Surname string `json:"surname"`
Cellphone int `json:"cellphone"`
AuxiliarCellphone int `json:"auxiliarCellphone"`
Email string `json:"email"`
Patients []*Patient `json:"patients" gorm:"many2many:professional_patient"`
}
and the get functions are
func GetAllPatients(c *gin.Context) {
var patients []models.Patient
internal.DB.Preload("Professionals").Find(&patients)
c.JSON(http.StatusOK, gin.H{"data": patients})
}
and
func GetAllProfessionals(c *gin.Context) {
var professionals []models.Professional
internal.DB.Preload("Patients").Find(&professionals)
c.JSON(http.StatusOK, gin.H{"data": professionals})
}
when i make a get request in postman for the professionals, the patients field return an empty array (which i think is fine because they don't have any patient loaded yet)
but in the case of the patients they return a "null" in the professionals field and the log in vscode says:
[2020-07-10 02:04:52] can't preload field Professionals for models.Patient
What can I do here?
We can Easy to get the child of hasMany relation in Golang Gorm with Preload.
But how to get reverse of relation.
type Owner struct {
ID int `gorm:"column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Projects []Project `gorm:"foreignkey:OwnerID" json:"projects"`
}
type Project struct {
ID int `gorm:"column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
OwnerID int `gorm:"column:owner_id" json:"owner_id"`
Gallery []Gallery `gorm:"foreignkey:ProjectID" json:"gallery"`
}
type Gallery struct {
ID int `gorm:"column:id" json:"id"`
ProjectID int `gorm:"column:project_id" json:"project_id"`
Url string `gorm:"column:url" json:"url"`
Title string `gorm:"column:title" json:"title"`
Description string `gorm:"column:description" json:"description"`
}
we can populate Gallery inside Project with Preload, like this:
db.Preload("Gallery").Find(&project)
How to get reverse, we want load project from gallery or owner from project?
the result I want some thing like this, when get project in form of json:
{
"id": 1,
"name": "Name Of Project",
"owner": {
"id":1,
"name": "Owner 1"
},
"gallery":[]
}
I am a bit late to the game with this answer, but you can define inverse relationships (belongs to) on the child models by doing this:
type ParentModel struct {
Children []Child // This is a 'has many'
}
type Child struct {
ParentModelID int // These behave as a 'belongs to'
ParentModel ParentModel // You need both the model and the modelID
}
Full working example
This uses the models from your question. A note on performance, the preloading in gorm isn't done via joins. It's done via additional SQL queries, the more nesting the more queries. If you have commonly executed preloading, it could be worth writing a raw query and a customer scanner to use an optimised query.
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Owner struct {
gorm.Model
Projects []Project
}
type Project struct {
gorm.Model
OwnerID int
Owner Owner
Gallery []Gallery
}
type Gallery struct {
gorm.Model
ProjectID int
Project Project
}
func main() {
db, err := gorm.Open(sqlite.Open("many2many.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
err = db.AutoMigrate(&Owner{}, &Project{}, &Gallery{})
if err != nil {
return
}
ownerOne := Owner{}
db.Create(&ownerOne)
projectOne := Project{Owner: ownerOne}
projectTwo := Project{Owner: ownerOne}
db.Create(&projectOne)
db.Create(&projectTwo)
galleryOne := Gallery{Project: projectOne}
galleryTwo := Gallery{Project: projectOne}
galleryThree := Gallery{Project: projectTwo}
galleryFour := Gallery{Project: projectTwo}
db.Create(&galleryOne)
db.Create(&galleryTwo)
db.Create(&galleryThree)
db.Create(&galleryFour)
// Find by project and preload owners
fetchedProject := Project{}
db.Preload("Owner").Find(&fetchedProject, projectOne.ID)
fmt.Println(fetchedProject.Owner)
// Find by a gallery and preload project and owner
fetchedGallery := Gallery{}
db.Preload("Project.Owner").Find(&fetchedGallery, galleryOne.ID)
fmt.Printf("Gallery.id = %d --> Project.id = %d --> Owner.id = %d\n", fetchedGallery.ID,
fetchedGallery.Project.ID, fetchedGallery.Project.Owner.ID)
}