One to many relationship with gorm in golang doesnt work - mysql

I have two tables:
type Person struct {
ID int
FirstName string
LastName string
Functions []Function
}
type Function struct {
gorm.Model
Info string
Person Person
}
I create the tables like this:
db.AutoMigrate(&models.Person{}, &models.Function{})
I then initialize the database:
user := models.Person{
FirstName: "Isa",
LastName: "istcool",
Functions: []models.Function{{Info: "Trainer"}, {Info: "CEO"}},
}
db.Create(&user)
Now the problem is that my Person table only got Firstname and Lastname columns and my Function table only got the Info column.
But when I start my GET request I get people with the column function which is always null.
Here is a screenshot from my GET request and my db
To see the code visit my GitHub repo

Finally found the answer!!
The problem is my GET functions I have to use
db.Preload("Functions").Find(&[]models.Person{})
instead of
db.Find(&[]models.Person{})

Related

MySQL seeded table is missing a column

I'm building a ReactJS/Node.js World Cup score prediction web app, and I just stumbled upon an odd problem. Apparently, when I seed my db, one column is missing.
I have a "seed.js" file containing data from every World Cup game. Each game is an object like this:
{
"gameTime": "2022-11-30T15:00:00Z",
"homeTeam": "tun",
"awayTeam": "fra",
"groupLetter": "d",
},
(I put a single game just for reference - there's 47 more like this one).
And here's the Prisma schema:
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
model User {
id String #id #default(cuid())
name String
email String #unique
username String #unique
password String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
guesses Guess[]
}
model Game {
id String #id #default(cuid())
homeTeam String
awayTeam String
gameTime DateTime
groupLetter String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
guesses Guess[]
##unique([homeTeam, awayTeam, gameTime, groupLetter])
}
model Guess {
id String #id #default(cuid())
userId String
gameId String
homeTeamScore Int
awayTeamScore Int
user User #relation (fields: [userId], references: [id])
game Game #relation (fields: [gameId], references: [id])
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
##unique([userId, gameId])
}
However, when I seed those games into my db, here's how each game object goes:
{
"id":"cladepd2o0011wh7ca47fo06f",
"homeTeam":"tun",
"awayTeam":"fra",
"gameTime":"2022-11-30T15:00:00.000Z",
"createdAt":"2022-11-12T04:07:07.632Z",
"updatedAt":"2022-11-12T04:07:07.632Z"
}
There's no "groupLetter" argument! And that's a crucial one because I use it as a filter, so my app shows the games one group at a time. And then, when it tries to fetch games by group letter, I get the following error:
[GET] /games?groupLetter=a
01:12:46:96
PrismaClientValidationError: Unknown arg `groupLetter` in where.groupLetter for type GameWhereInput.
at Document.validate (/var/task/node_modules/#prisma/client/runtime/index.js:29297:20)
at serializationFn (/var/task/node_modules/#prisma/client/runtime/index.js:31876:19)
at runInChildSpan (/var/task/node_modules/#prisma/client/runtime/index.js:25100:12)
at PrismaClient._executeRequest (/var/task/node_modules/#prisma/client/runtime/index.js:31883:31)
at async PrismaClient._request (/var/task/node_modules/#prisma/client/runtime/index.js:31812:16)
at async list (file:///var/task/api/games/index.js:14:23)
at async bodyParser (/var/task/node_modules/koa-bodyparser/index.js:95:5)
at async cors (/var/task/node_modules/#koa/cors/index.js:108:16) {
clientVersion: '4.4.0'
}
I already tried to drop the "Game" table via PlanetScale and push it again, to no avail. I just don't understand why "groupLetter" is missing from the prod database.
Oh, locally it's all working fine (the app shows the games sorted by group, exactly how it's supposed to be).
Hope I didn't make it even more confusing that it is. Can you guys please help me out?
Nevermind... I realized, after staying up all night in front of the screen researching and doing a lot of trial and error, that I didn't redo npx prisma generate procedures. Problem solved now.

Custom fields in Many2Many JoinTable

I have this model with a custom JoinTable:
type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}
type Address struct {
ID uint
Name string
}
type PersonAddress struct {
PersonID int
AddressID int
Home bool
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
How is it possible to assign a value to the Home field when creating a new Person?
Method 1
From what I can see in the docs, here's a clean way you might currently do this:
DB.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
addr1 := Address{Name: "addr1"}
DB.Create(&addr1)
addr2 := Address{Name: "addr2"}
DB.Create(&addr2)
person := Person{Name: "jinzhu"}
DB.Create(&person)
// Add an association with default values (i.e. Home = false)
DB.Model(&person).Association("Addresses").Append(&addr1)
// Add an association with custom values
DB.Create(&PersonAddress{
PersonID: person.ID,
AddressID: addr2.ID,
Home: true,
})
Here we're using the actual join table model to insert a row with the values we want.
We can also filter queries for the association:
addr := Address{}
// Query association with filters on join table
DB.Where("person_addresses.home = true").
Model(&person).
Association("Addresses").
Find(&addr)
Method 2
Here's a more magical way, by (ab)using the Context to pass values to a BeforeSave hook, in addition to the SetupJoinTable code from above:
func (pa *PersonAddress) BeforeSave(tx *gorm.DB) error {
home, ok := tx.Statement.Context.Value("home").(bool)
if ok {
pa.Home = home
}
return nil
}
// ...
DB.WithContext(context.WithValue(context.Background(), "home", true)).
Model(&person).
Association("Addresses").
Append(&addr2)
This method feels icky to me, but it works.
As you can find this point in the official documents of the GROM, you can implement some methods for each table(struct).
You can implement BeforeCreate() and/or AfterCreate() methods for your join table, gorm will check that method on time!
You can do anything inside those methods to achieve your goal.
here you will find the full documentation.
enjoy ;)

How to pass dynamic table name in gorm model

I am using Gorm ORM for my current application. I have one model correspondents to many tables with identical table structures(i.e. column name and type). So my requirement how can I change the table name dynamically while doing the query.
For e.g.
I have product model like Product.go
type Product struct{
ID int
Name strig
Quantity int
}
And we have different products like shirts, jeans and so on and we have same tables like shirts, jeans.
Now I wanted to query the product as per name of the product how can we do that also want to have table created through migrations. But there is only One model than how can we run use Automigrate feature with Gorm.
Updated for GORM v2
DEPRECATED: TableName will not allow dynamic table name anymore, its result will be cached for future uses.
There is a much easier way to create several tables using the same struct:
// Create table `shirts` & `jeans` with the same fields as in struct Product
db.Table("shirts").AutoMigrate(&Product{})
db.Table("jeans").AutoMigrate(&Product{})
// Query data from those tables
var shirts []Product
var jeans []Product
db.Table("shirts").Find(&shirts)
db.Table("jeans").Where("quantity > 0").Find(&shirts)
But, now on the second thought, I would suggest using the embedded struct so that you won't have to call Table in every query and you can also have additional fields per model while still sharing the same table structure.
type ProductBase struct {
ID int
Name strig
Quantity int
}
type Shirt struct {
ProductBase
NeckType string
}
type Jean struct {
ProductBase
Ripped bool
}
db.AutoMigrate(&Shirt{}, &Jean{})
shirt, jeans := Shirt{}, make([]Jean, 0)
db.Where("neck_type = ?", "Mandarin Collar").Last(&shirt)
db.Where("ripped").Find(&jeans)
Old answer for GORM v1
You're almost there by using table field inside the struct:
type Product struct{
ID int
Name strig
Quantity int
// private field, ignored from gorm
table string `gorm:"-"`
}
func (p Product) TableName() string {
// double check here, make sure the table does exist!!
if p.table != "" {
return p.table
}
return "products" // default table name
}
// for the AutoMigrate
db.AutoMigrate(&Product{table: "jeans"}, &Product{table: "skirts"}, &Product{})
// to do the query
prod := Product{table: "jeans"}
db.Where("quantity > 0").First(&prod)
Unfortunately, that does not work with db.Find() when you need to query for multiple records... The workaround is to specify your table before doing the query
prods := []*Product{}
db.Table("jeans").Where("quantity > 0").Find(&prods)

golang -> gorm: How I can use sql.NullInt64 to be int(10) in mysql?

type Contact struct {
gorm.Model
PersonID sql.NullInt64
}
type Person struct {
gorm.Model
}
I am trying to use gorm with mysql in the previuos code but I have the following problem:
I want:
Use sql.NullInt64 to work easily with null values.
Use the base model definition gorm.Model, including fields ID, CreatedAt, UpdatedAt, DeletedAt.
Add a constraint Db.Model(&models.Contact{}).AddForeignKey.
My problem:
Person.ID become "int(10)" in mysql.
Contact.PersonID become "bigint(20)"
MySql need the same type for pk and fk.
Some body can help me to solve this?
The "magic" on gorm.Model is only the name of the fields, any struct with these fields look like this according to the gorm documentation, at the end of Conventions
For example: Save records having UpdatedAt field will set it to current time.
Or
Delete records having DeletedAt field, it won't be deleted from database, but only set field DeletedAt's value to current time, and the record is not findable when querying, refer Soft Delete
So solve the issue is very easy, this is the code for my case:
package models
import "time"
type Model struct {
ID uint `gorm:"primary_key;type:bigint(20) not null auto_increment"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
So, now I only need use it as base model :)

Create table using Go-Gorp fails to set column details

Trying to create the table using Gorp-Go ORM package. Was able to successfully create the table in MySql but failed to attach column details.
type Data struct {
id int `db:"pid"`
name string `db:",size:50"`
}
Gorp hook
Dbm.AddTableWithName(Data{}, "data_test").SetKeys(true, "id")
Dbm.CreateTablesIfNotExists()
Dbm is pointer to gorp.DbMap. The resultant table has pid and ,size:50 has name. Have tried with
type Data struct {
id int `db:"pid"`
name string `db:"name:xyz,size:50"`
}
Still the resultant column name is "name:xyz,size:50"
According to this comment, the size feature is still available in only dev branch.
You can achieve this by explicitly setting maxsize though. Example:
dt := Dbm.AddTableWithName(Data{}, "data_test").SetKeys(true, "id")
dt.ColMap("xyz").SetMaxSize(50)
Dbm.CreateTablesIfNotExists()
....
I believe the column name doesn't require "name"
Try db:"xyz,size:50"