Cannot read "one to many" Relation with go-pg - postgresql

I am trying to implement a little state machine with go and store my states in a postgres db.
i created my database like this:
CREATE TABLE state_machines
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
initial_state TEXT NOT NULL,
"name" TEXT NOT NULL
);
CREATE TABLE state_machine_states
(
state_machine_id uuid NOT NULL REFERENCES state_machines(id) ON DELETE CASCADE,
"name" TEXT NOT NULL,
PRIMARY KEY(state_machine_id, "name")
);
// StateMachine is the DB schema
type StateMachine struct {
ID *uuid.UUID `pg:"id,pk,type:uuid,default:uuid_generate_v4()"`
Name string `pg:"name"`
InitialState string `pg:"initial_state"`
States []*StateMachineState `pg:"fk:state_machine_id"`
}
// StateMachineState is the DB schema
type StateMachineState struct {
StateMachineID uuid.UUID `pg:"state_machine_id,fk"`
Name string `pg:"name"`
}
I am using go-pg 9.2 and i am trying to load a state machine and a list of its states from the "States" relation.
My function to load the state machines looks like this:
func (cep *repository) GetStateMachines() ([]*StateMachine, error) {
stateMachines := []*StateMachine{}
err := cep.db.Model(&stateMachines).
Relation("States").
Relation("Transitions").
Select()
return stateMachines, err
}
If I execute it, I always get the error message Error reading state machine: model=StateMachine does not have relation="States"
I have done similar relations before and they worked and now, I cannot get it to work again :(

Try upgrading to v10 which fully supports relations: https://pg.uptrace.dev/orm/has-many-relation/

See if there is .Debug() function in go-pg for a query.
I use https://gorm.io/ , and there is a Debug funcion, that returns all of the SQL queries.
When i see what queries are send, i log in postgresql and try them manually to see a more detailed error.

Related

Using go's sqlx to insert record in postgres table with automatic ID generation

I am using sqlx to create a go api.
I want to insert a record in a table named day.
The corresponding go struct is the following
type Day struct {
ID string `db:"id" json:"id"`
Dateday string `db:"dateday" json:"dateday"`
Nameday string `db:"nameday" json:"nameday"`
Holyday bool `db:"holyday" json:"holyday"`
}
In an endpoint for Day creation, will be receiving all fields but the ID via a post request
What method should I use to interact with my db so as to:
a) create the record
b) not need to pass the ID myself and instruct postgres to auto-generate the field.
The table creation statement is the following:
CREATE TABLE IF NOT EXISTS "day" (
"id" SERIAL PRIMARY KEY,
"dateday" date NOT NULL,
"nameday" varchar(10) NOT NULL,
"holyday" boolean NOT NULL
);
I would suggest to override the MarshalJSON method as mentioned below:
func (r Day) MarshalJSON() ([]byte, error) {
root := make(map[string]interface{})
root["dateday"] = r.Dateday
root["nameday"] = r.Nameday
root["holyday"] = r.Holyday
return json.Marshal(root)
}
Ref: https://golang.org/pkg/encoding/json/#example__customMarshalJSON

How to avoid that 0 (zero) int turns into Postgres "null" value and violates "not null" constraint?

In Go, I am unmarshalling/decoding JSON into a struct with an ID field of type int. Then I try to insert this struct into a PostgreSQL database using go-pg with the ID column as the primary key (which has a not-null constraint). The first entry has a 0 as its ID. In the Postgres documentation, it states that 0 is ok as a value of a primary key. However, I keep getting an error message:
"ERROR #23502 null value in column "number" violates not-null constraint".
It looks like the 0 turns into a Go "zero value" when it is unmarshalled into the int value. Then it is inserted as null value into Postgres. Any tips on how I might be able to avoid this would be greatly appreciated.
type Account struct {
Number int `sql:"type:smallint, pk"`
Name string
}
[...]
account := Account{}
err := json.NewDecoder(r.Body).Decode(&account)
[...]
insertErr := pgLayer.db.Insert(&account)
if insertErr != nil {
log.Printf("Error while inserting new item")
return "n/a", insertErr
}
While it's not immediately obvious with go-pg you can use the struct tag sql:",notnull" to show that Go empty values ("", 0, [] etc.) are allowed and should not be treated as SQL NULL.
You can see it in the Features list.
In your case I would change this to:
type Account struct {
Number int `sql:"type:smallint,pk,notnull"`
Name string
}
I think the easiest solution to your problem is to make your ID column of type SERIAL and let Postgres deal with setting and auto-incrementing the value for you. If you need the value within your application directly after inserting it, you can always use a RETURNING psql clause, like such:
INSERT INTO shows(
user_id, name, description, created, modified
) VALUES(
:user_id, :name, :created, :modified
) RETURNING id;
And capture the response within your code.

Foreign key not created with one to many association

I have a Task type that has a list of Runner type objects in it. I am trying to map it to database using golang gorm but it doesn't have foreign key and i am getting invalid association during migration
My Task struct:
type Task struct {
gorm.Model
Name string `gorm:"not null;unique_index"`
Description string
Runners []Runner
}
My Runner struct:
type Runner struct {
gorm.Model
Name string `gorm:"not null;unique"`
Description string
}
My migration code:
func migrateSchema () (err error) {
db, err := context.DBProvider()
if err != nil {
return
}
db.Model(&Task{}).Related(&Runner{})
db.AutoMigrate(&Task{})
db.AutoMigrate(&Runner{})
return
}
On db.AutoMigrate(&Task{}) I get invalid association message in console and when I check the database there is no foreign key created or no reference field created on runners table
What am I doing wrong?
I had a similar issue, and it took me forever to figure it out. I believe the GORM documentation could definitely be better. Here's the relevant code snippet from the GORM site:
//User has many emails, UserID is the foreign key
type User struct {
gorm.Model
Emails []Email
}
type Email struct {
gorm.Model
Email string
UserID uint
}
db.Model(&user).Related(&emails)
//// SELECT * FROM emails WHERE user_id = 111; // 111 is user's primary key
Why your code isn't working:
First you need to add a TaskID field to your Runner struct.
db.Model(&Task{}).Related(&Runner{}) doesn't do what you think it does. If you look at the code snippet from GORM, the SELECT comment kind of explains it (not very well though). The example is assuming that the &user is already populated and has an ID of 111, then it fetches the emails storing them in &emails that match the UserID of &user.

inserting relationships go-pg PostgreSQL

I've got 2 structs to represent a ManyToMany relationship. User and Note
type User struct {
ID int
Name string
Notes []*Note
}
type Note struct {
TableName struct{} `sql:"user_notes"`
ID int
Text string
}
Now let's say I want to insert a new user and also at the same time add a few notes.
I would expect this to insert a user and its note(s):
note := Note{
Text: "alohaa dude",
}
user := User{
Name: "peter",
Notes: []Note{no},
}
s.DB.Insert(&user)
However this only saves the user and not the user and the note. In go-pg do I have to do this manually or is there an automated way through the ORM?
Rodrigo, same problem statement is being discussed here: https://github.com/go-pg/pg/issues/478
This functionality is not supported in go-pg at this time and you might want to try a db prepare approach to insert with relationships.

Golang: gorm use Find(&model) for non gorm migrate table

There is table customer_account (postgres) which one was migrate from YII2.
DDL:
CREATE TABLE public.test_table (
id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('test_table_id_seq'::regclass),
data JSONB
);
In go project i try to get value from this table.
type TableGo struct {
Id int
Data string `gorm:"type:jsonb"`
}
table := TableGo{}
db.Where("id = ?", 75).Find(&table)
println(table.Data)
But there is (pq: relation "table_gos" does not exist)
How i can link structure which table without db.AutoMigrate(&TableGo{})?
I think table name in your migration script is wrong. Because it is not in GORM convention. If you want to use that name,you can use following method in your model for custom table name.
func (m *Model) TableName() string {
return "custom_table_name"
}
Found the solution:
func(TableGo) TableName() string {
return "account_status"
}