Struggling to manually create relations with gorm / migrate - postgresql

I have an sql structure like so:
CREATE TABLE resources (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
deleted_at TIMESTAMPTZ
);
CREATE TABLE tags (
name TEXT PRIMARY KEY
);
What sql do I need to write, how can I tell gorm that I want a Resource to have many Tags? This is what I have currently go-wise:
package models
import (
"github.com/jinzhu/gorm"
)
type Tag struct {
Name string `gorm:"PRIMARY_KEY"`
}
type Resource struct {
gorm.Model
Title string
Tags []Tag `gorm:""`
}
Note that I explicitly do not want to auto-migrate via gorm. I am using the migrate tool to handle migrations and want to specifically handle them manually, not with go.

To define a has many relationship, a foreign key must exist.
So for your case Tag should be:
type Tag struct {
gorm.Model
Name string `gorm:"PRIMARY_KEY"`
ResourceID int
}
and Resource:
type Resource struct {
gorm.Model
Title string
Tags []Tag `gorm:"foreignkey:ResourceID"`
}
And your sql structure should have that foreign key ResourceID column.
Not sure if you have already checked this but it contains more details in case you need them: https://gorm.io/docs/has_many.html#Foreign-Key

Related

Diesel migration force String instead of Uuid in schema.rs

I have a problem with the diesel migration. I need to implement Uuid as primary key for a model. I got a lot of issues with the Uuid integration (feature uuidv07, uuid crate,..) but when I specify the type uuid in the migration, diesel generate a "Varchar" field in the migration, so I can't use Uuid as a type of field in my model.
users.sql
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR NOT NULL,
name VARCHAR NOT NULL,
password VARCHAR NOT NULL,
id_role INT,
CONSTRAINT fk_role
FOREIGN KEY(id_role)
REFERENCES roles(id)
)
schema.rs
table! {
users (id) {
id -> Varchar,
email -> Varchar,
name -> Varchar,
password -> Varchar,
id_role -> Nullable<Int4>,
}
}
Is this normal to use Varchar and not Uuid ?
uuid = { version = "0.8.2", features = ["serde", "v4"] }
diesel = { version = "1.4.5", features = ["postgres", "r2d2", "uuidv07"] }
Thanks.
Diesel does not force anything onto you. It just reads the types from your database system. So if it outputs a Varchar as type for a specific column that means your database system recorded Varchar (for whatever reason) as type for this column. Or to word it differently: The problem here is not diesel, but likely an interaction between your migration, your existing database schema and your database. As neither information about your pre-existing database schema nor about your database system is provided as part of your question there is not much I can add here to point you into the right direction.

Golang Gorm Automigrate not create filed type "time"

My struct looks like:
type AdvertContent struct {
Id string `gorm:"column:id;primaryKey;type:uuid;default:uuid_generate_v4()" json:"id" example:"4ff8eb91-640b-4e26-a50f-3bcd1f933d0c"`
FromTime *time.Time `gorm:"column:from_time;type:time;" json:"fromTime,omitempty" example:"HH:MM"`
ToTime *time.Time `gorm:"column:to_time;type:time;" json:"toTime,omitempty" example:"HH:MM"`
} //#name AdvertContent
func (this AdvertContent) TableName() string {
return "advert_content"
}
When I use gorm.AutoMigrate, table fields from_time and to_time created with type timestampz, not time.
Gorm debug mode:
CREATE TABLE "advert_content" ("id" uuid DEFAULT uuid_generate_v4(),"from_time" timestamptz,"to_time" timestamptz)
How can I create table with time type fields?
when using specified database data type, it needs to be a full
database data type, for example: MEDIUMINT UNSIGNED NOT NULL
AUTO_INCREMENT
You should use the database data type like this

How GORM reads the value of the alias field

When querying data, an alias is set for a field. How do I use GORM to read the value of the alias field into the structure?
Table Structure
DROP TABLE IF EXISTS "test"."test";
CREATE TABLE "test"."test" (
"id" varchar(32) NOT NULL,
"name" varchar(255) COLLATE "pg_catalog"."default",
"remark" varchar(255) COLLATE "pg_catalog"."default"
);
ALTER TABLE "test"."test" ADD CONSTRAINT "test_pkey" PRIMARY KEY ("id");
Table corresponding model structure
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
MoreInfo string `gorm:"-" json:"moreInfo"` // Non-table field
}
GORM query
gormDB is the initialized gorm database connection.
test := Test{ID: "0000000001"}
gormDB.Select("*, 'testMoreInfoVal' AS more_Info").Where(&test).Find(&test)
Q: How to use GORM to read the alias field more_Info that does not exist in the table into the MoreInfo property of the Test structure?
If the table structure is set and you aren't using AutoMigrate then this is solved by just changing your tags to make MoreInfo a read-only field and making sure you use the alias more_info to match the way Gorm does DB -> Go naming translation.
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
MoreInfo string `gorm:"->" json:"moreInfo"` // Non-table field
}
gormDB.Select("*, 'testMoreInfoVal' AS more_info").Where(&test).Find(&test)
If you are using AutoMigration then the problem will be that a more_info column will be created in your table, though Gorm will prevent writing to that column when using the struct.
What you could do in that case is use a new struct which embeds the Test struct like so:
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
}
type TestExt struct{
Test
MoreInfo string `gorm:"->" json:"moreInfo"`
}
testext := TestExt{}
gormDB.Model(&Test{}).
Select("*, 'testMoreInfoVal' AS more_info").
Where(Test{ID: "0000000001"}).
Find(&testext)

Prisma2 prisma introspect returning weird values for foreign keys

I have two tables, User and Relationship. The tables are used to store a parent child relationship. I am using Postgres
// schema.sql
CREATE TABLE "public"."Relationships" (
id SERIAL PRIMARY KEY NOT NULL,
parent_id INT NOT NULL,
FOREIGN KEY (parent_id) REFERENCES "User" (id),
child_id INT NOT NULL,
FOREIGN KEY (child_id) REFERENCES "User" (id)
)
CREATE TABLE "public"."User" (
id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(128) NOT NULL,
email VARCHAR(128) UNIQUE,
password VARCHAR(128) NOT NULL,
isChild BOOLEAN NOT NULL DEFAULT false
created_at TIMESTAMP NOT NULL DEFAULT NOW();
);
When I run npx prisma introspect the following is returned in the schema.prisma file.
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Relationships {
child_id Int
id Int #default(autoincrement()) #id
parent_id Int
User_Relationships_child_idToUser User #relation("Relationships_child_idToUser", fields: [child_id], references: [id])
User_Relationships_parent_idToUser User #relation("Relationships_parent_idToUser", fields: [parent_id], references: [id])
}
model User {
created_at DateTime #default(now())
email String? #unique
id Int #default(autoincrement()) #id
ischild Boolean #default(false)
name String?
password String
Relationships_Relationships_child_idToUser Relationships[] #relation("Relationships_child_idToUser")
Relationships_Relationships_parent_idToUser Relationships[] #relation("Relationships_parent_idToUser")
}
I dont understand what User_Relationships_child_idToUser and User_Relationships_parent_idToUser are and why they are not just the simple syntax that appears for foreign keys in the Prisma docs tutorial.
Prisma's introspection generates two fields for each foreign key:
the relation scalar field (basically the direct representation of the foreign key)
a relation field (annotated with the #relation attribute) – these are the fields that seem to confuse you. The reason they provide is such that you can work with the relations easily in the Prisma Client API.
You can find more info about this in the docs here.
Of course User_Relationships_parent_idToUser and User_Relationships_child_idToUser are not very nice names. You can manually adjust the schema after introspection and rename the relation fields to have friendlier names, e.g:
model Relationships {
child_id Int
id Int #default(autoincrement()) #id
parent_id Int
child User #relation("Relationships_child_idToUser", fields: [child_id], references: [id])
parent User #relation("Relationships_parent_idToUser", fields: [parent_id], references: [id])
}

`gorm` Ignoring `sql:"index"` Tags

Why gorm is ignoring sql:"index" tags? No indexes got created.
Database in use here is PostgreSQL (importing _ "github.com/lib/pq"). This Model struct is used (because default gorm.Model uses an auto increment number - serial - as primary key and I wanted to set id myself):
type Model struct {
ID int64 `sql:"type:bigint PRIMARY KEY;default:0"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
And one of actual models is:
type TUHistory struct {
Model
TUID int64 `json:"tu_id,string" gorm:"column:tu_id" sql:"index"`
}
func (x *TUHistory) TableName() string {
return "tu_history"
}
And the table is created by db.CreateTable(&TUHistory{}) which creates the table correctly except for indexes.
As a temporary work around, I do db.Model(&TUHistory{}).AddIndex("ix_tuh_tu_id", "tu_id") to create indexes.
From my experience, the db.CreateTable only creates the table and it's fields. You are better off using the AutoMigrate function with the model structure that you want to migrate:
db, err := gorm.Open("postgres", connectionString)
...
// error checking
...
db.AutoMigrate(&Model)
Also, I tried AutoMigrating the model you posted and got an error saying that multiple primary keys are not allowed, so I changed the model to:
type Model struct {
Id int64 `sql:"type:bigint;default:0"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
and the AutoMigration created all PKs and indexes just fine.
Edit:
Checking the GORM's README, on this example, the Email structure goes as:
type Email struct {
ID int
UserID int `sql:"index"` // Foreign key (belongs to), tag `index` will create index for this field when using AutoMigrate
Email string `sql:"type:varchar(100);unique_index"` // Set field's sql type, tag `unique_index` will create unique index
Subscribed bool
}
Notice the comment on the UserId field saying it will create the index when using AutoMigrate.
Also, it's worth taking a look at how the AutoMigrate does it's job:
// Automating Migration
db.AutoMigrate(&User{})
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
// Feel free to change your struct, AutoMigrate will keep your database up-to-date.
// AutoMigrate will ONLY add *new columns* and *new indexes*,
// WON'T update current column's type or delete unused columns, to protect your data.
// If the table is not existing, AutoMigrate will create the table automatically.