Using Different Join Table Columns than Defaults with GORM many2many join - postgresql

I'm using gorm to manage my struct to database mappings.
Right now, I have an existing schema that I'm trying to wrap in an api.
The issue is that in one of my structs, I have a many2many join through another table which doesn't use the default column names.
golang code
type AuthUser struct {
ID *int
Username *string `sql:"size:30"`
FirstName *string `sql:"size:30"`
LastName *string `sql:"size:30"`
Email *string `sql:"size:75"`
IsActive *bool
IsStaff *bool
IsSuperuser *bool
DateJoined *time.Time
LastLogin *time.Time
//ReviewsGroups ReviewsGroups `gorm:"foreignkey:group_id;associationforeignkey:user_id;many2many:reviews_group_users"`
//ReviewsGroups ReviewsGroups `gorm:"many2many:reviews_group_users"`
ReviewsGroups []ReviewsGroup `gorm:"many2many:reviews_group_users"`
}
type ReviewsGroups []ReviewsGroup
func (app *AppEnv) FindAuthUser(username string) (*AuthUser, error) {
var user AuthUser
//var groups []ReviewsGroup
//err := app.DB.Where(&AuthUser{Username: &username}).First(&user).Related(&groups).Error
err := app.DB.Preload("ReviewsGroups").Where(&AuthUser{Username: &username}).First(&user).Error
return &user, err
}
join table
rb=> \d reviews_group_users
Table "public.reviews_group_users"
Column | Type | Modifiers
----------+---------+------------------------------------------------------------------
id | integer | not null default nextval('reviews_group_users_id_seq'::regclass)
group_id | integer | not null
user_id | integer | not null
Indexes:
"reviews_group_users_pkey" PRIMARY KEY, btree (id)
"reviews_group_users_group_id_user_id_key" UNIQUE CONSTRAINT, btree (group_id, user_id)
"reviews_group_users_group_id" btree (group_id)
"reviews_group_users_user_id" btree (user_id)
Foreign-key constraints:
"group_id_refs_id_0f32ed01" FOREIGN KEY (group_id) REFERENCES reviews_group(id) DEFERRABLE INITIALLY DEFERRED
"reviews_group_users_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
resulting query output
[2015-10-30 09:31:32] [1.16ms] SELECT * FROM "auth_user" WHERE ("username" = 'theuser') ORDER BY "auth_user"."id" ASC LIMIT 1
[2015-10-30 09:31:32] [1.85ms] SELECT * FROM "reviews_group" INNER JOIN reviews_group_users ON reviews_group_users."reviews_group_id" = "reviews_group"."id" WHERE (auth_user_id in ('13'))
[2015-10-30 09:31:32] pq: column reviews_group_users.reviews_group_id does not exist
Any help is appreciated.
Thanks!

I think I can refer you to this answer => gorm Many2Many Custom table
Split the relationship into 3 tables or 3 structs if you like. AuthUser, ReviewGroup, and Group.
In your auth user you do the same as you would do with a one-to-many relationship and the same for group.
example:
type AuthUser struct {
ID *int
Username *string `sql:"size:30"`
FirstName *string `sql:"size:30"`
LastName *string `sql:"size:30"`
Email *string `sql:"size:75"`
IsActive *bool
IsStaff *bool
IsSuperuser *bool
DateJoined *time.Time
LastLogin *time.Time
ReviewsGroups []ReviewsGroup `gorm:"foreignKey:User_id;references:ID;"`
}
type Group struct {
ID uint `gorm:"primaryKey"`
param string
ReviewsGroups []ReviewsGroup
}
type ReviewGroup struct {
User_id uint `gorm:"primaryKey"`
User AuthUser `gorm:"foreignKey:ID;references:User_id"` // not necessary unless you want to be able to navigate via this model
Group_id uint `gorm:"primaryKey"`
Group AuthUser `gorm:"foreignKey:ID;references:Group_id"` // not necessary unless you want to be able to navigate via this model
param string
}
Hope it helps!

Related

Gorm not reading columns on select

I'm running a SELECT using WHERE on gorm but the results are comming with the columns store_name and type empty(""). I have the following struct on Go:
type Store struct {
ID uint
StoreName string
Type string
Code string `gorm:"unique"`
Active bool
CreatedAt *time.Time
UpdatedAt *time.Time
}
The following database on Postgres:
CREATE TABLE IF NOT EXISTS stores
(
id BIGSERIAL PRIMARY KEY,
store_name TEXT NOT NULL,
type TEXT NOT NULL,
code TEXT NOT NULL UNIQUE,
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
Im running this select right here:
var store Store
result := db.First(&stores).Where("code = ?", code)
if err := result.Error; err != nil {
return nil, err
}
return &stores, nil
Does anyone know what I may be doing wrong? All columns are returned on the SELECT except for the columns StoreName and Type. Thank you so much in advance!
You need to set the field tag if field name differs from DB column name.
Example:
StoreName string `gorm:"column:store_name"`
See this document: https://gorm.io/docs/models.html

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])
}

Why GORM doesn't generate FOREIGN CONSTRAINT for Postgres?

I'm new with Go and GORM. After generating my DB for testing, I don't see that the One To Many relationship is properly represented in Postgres.
I have created a couple of models to be migrated into Postgres, for example, these 2 specifically:
type PgPiece struct {
ID string `gorm:"primary_key"`
Name string
MinPrice float64
Likes int
Description string
Materials string
Techniques string
Width float64
Height float64
Length float64
Hours int
CreatedAt *time.Time
ModifiedAt *time.Time
IsFavorite bool
EstimatedValue float64
Owner PgUser `gorm:"foreignkey:OwnerID"`
OwnerID string
Author PgUser `gorm:"foreignkey:AuthorID"`
AuthorID string
Favorites []PgUser `gorm:"many2many:user_favorite_piece"`
Images []PgPieceImage `gorm:"foreignkey:piece_id"`
}
type PgPieceImage struct {
ID string `gorm:"primary_key"`
Url string
Position int
Width int
Height int
CreatedAt *time.Time
Piece PgPiece `gorm:"foreignkey:PieceRef"`
PieceRef string `gorm:"column:piece_id"`
}
And as result I see this in postgres for PgPieceImage
CREATE TABLE public.piece_image
(
id text COLLATE pg_catalog."default" NOT NULL,
url text COLLATE pg_catalog."default",
"position" integer,
width integer,
height integer,
created_at timestamp with time zone,
piece_id text COLLATE pg_catalog."default",
CONSTRAINT piece_image_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.piece_image
OWNER to freddy;
Why there is non FOREIGN CONSTRAINT there?

Store recursive go structs in Postgresql database

I have two structs (Person and Tenant) that reference each other recursively.
I have no experience with 'SQL' and Im trying to use the https://github.com/jmoiron/sqlx library to store these structs in a way that they keep referencing each other, so that I can retrieve them again as structs.
I don't know which type should the tables be created with or how I am supposed to insert the objects to get it work.
Also If there is any other go library that can handle this case easily i'm open to any suggestions.
Thanks in advance.
type Tenant struct {
Id int `db:"id"`
Name string `db:"name"`
Person []Person `db:"person"`
}
type Person struct {
Id int `db:"id"`
Username string `db:"username"`
Tenants *[]Tenant `db:"tenants"`
}
func main() {
var schema = `
CREATE TABLE IF NOT EXISTS person (
id int,
username text
tenants []text //-> type????
);
CREATE TABLE IF NOT EXISTS tenant (
id int,
name text,
person []text //-> type????
)`
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=%s",
host, port, user, password, dbname, sslmode)
db, err := sqlx.Open("postgres", psqlInfo)
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
db.MustExec(schema)
var tenant1 Tenant
var person1 Person
tenant1 = Tenant{1, "newtenant", []Person{person1}}
person1 = Person{1, "newuser", &[]Tenant{tenant1}}
tx := db.MustBegin()
tx.NamedExec("INSERT INTO tenant (id,name,person) VALUES (:id,:name, :person)", &tenant1)
tx.Commit()
out := []Tenant{}
db.Select(&out, "SELECT * FROM tenant ORDER BY name ASC")
fmt.Println(out)
}
NOTE: This is not a real answer, just a longer comment on the SQL part of the question. Unfortunately I have no experience with sqlx so I cannot help you with that.
What you have there seems to be a many-to-many relationship. A Person can belong to multiple Tenants and a Tenant can have multiple Persons.
In SQL this is usually handled by, what's sometimes called, a linking or junction table.
-- postgresql flavor of SQL
CREATE TABLE IF NOT EXISTS person (
id serial PRIMARY KEY,
username text NOT NULL
);
CREATE TABLE IF NOT EXISTS tenant (
id serial PRIMARY KEY,
name text NOT NULL
);
-- the linking table
CREATE TABLE IF NOT EXISTS person_tenant (
person_id integer NOT NULL REFERENCES person (id),
tenant_id integer NOT NULL REFERENCES tenant (id),
PRIMARY KEY(person_id, tenant_id)
);

Grails doesn't create foreign key column in PostgreSQL

I am having trouble generating my tables in PostgreSQL from Grails. I have simple Email and EmailAttachment domain classes with a hasMany and belongsTo relationship. This setup worked well on our production server (AS400 DB2), but when I try to run my program on PostgreSQL (the new dev environment), the Email class does not have the attachment_id column.
Email.groovy:
class Email {
static hasMany = [attachments:EmailAttachment]
Integer id
Integer version = 0
String subject
String recipients
String sender
Date sentDate
String plainTextMessage
Set attachments
static mapping = {
datasources(['DEFAULT'])
table name:'error_email', schema: Appointment.schema
sort sentDate:'desc'
}
static constraints = {
subject nullable:true
version nullable:true
recipients nullable:true
sender nullable:true
sentDate nullable:true
plainTextMessage nullable:true
attachments nullable:true
}
def String toString(){
return subject
}
}
EmailAttachment.groovy:
class EmailAttachment {
static belongsTo = [email:ErrorEmail]
ErrorEmail email
String filename
byte[] content
static mapping = {
datasources(['DEFAULT'])
table name:'error_email_attachment', schema: Appointment.schema
}
static constraints = {
filename nullable:true
content nullable:true
}
}
Also, here are the relevant lines from schema-export:
alter table program.email_attachment drop constraint FK2E592AFD1D80E229;
drop table program.email cascade;
drop table program.email_attachment cascade;
drop sequence hibernate_sequence;
create table program.email (id int4 not null, version int4, plain_text_message varchar(255), recipients varchar(255), sender varchar(255), sent_date timestamp, subject varchar(255), primary key (id));
create table program.email_attachment (id int8 not null, version int8 not null, content bytea, email_id int4 not null, filename varchar(255), primary key (id));
alter table program.email_attachment add constraint FK2E592AFD1D80E229 foreign key (email_id) references program.error_email;
create sequence hibernate_sequence;
I've tried specifying joinTable: attachments joinTable:[name: 'email_table', column: 'attachment_id', key: 'id'] to no avail, as well as leaving it out, and trying other collection types for attachment. Thanks in advance for your time and brain cells.
The email doesn't have an attachment_id column because it's the one side of the one-to-many. The many side, attachment in this case, has a reference to its owning email in the email_id int4 not null column. Given an email with id 42 you (and Grails/GORM/Hibernate) can find all of the attachments by querying for all rows in that table with email_id=42.