so I have a database which 3 tables: creditor, debtor and operation, the operation are refer to a creditor and a debtor if this debtor is already in the table I dont want that create a new row, the same for debtor, but the operation have to be storage with the id of the already in database debtor or/and creditor.
So for try to make this I put the uniqueIndex:idx_name which only storage the data if both (name/bic or name/iban) are unique, so that its good, but the problem is gorm give this error and doesnt storage the operation.
Error 1452: Cannot add or update a child row: a foreign key constraint fails (`mybb`.`operations`, CONSTRAINT `fk_operations_creditor` FOREIGN KEY (`creditor_id`) REFERENCES `creditors` (`id`))
Is there any way to do this? or do I have to separately check if the debtor and creditor exist, save it if not or get the id if yes and later save the operation?.
Structures
type Creditor struct {
gorm.Model
Name string `json:"name" gorm:"uniqueIndex:idx_name"`
BIC string `json:"bic" gorm:"uniqueIndex:idx_name"`
}
type Operation struct {
gorm.Model
Debtor *Debtor
Creditor *Creditor
Type int `json:"type"`
Gateway string `json:"gateway"`
DebtorID int `json:"debtor_id"`
CreditorID int `json:"creditor_id"`
}
type Debtor struct {
gorm.Model
Name string `json:"name" gorm:"uniqueIndex:idx_name"`
IBAN string `json:"iban" gorm:"uniqueIndex:idx_name"`
}
Code
var err error
dsn := "root:changeme#tcp(localhost:3306)/mybb?charset=utf8mb4&parseTime=True&loc=Local"
conn, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
println(err.Error())
}
conn.AutoMigrate(Operation{})
op := make([]Operation, 0)
op = append(op, Operation{
Debtor: &Debtor{
IBAN: "test",
Name: "hey"},
Creditor: &Creditor{
Name: "otr",
BIC: "hello"},
Type: 2,
Gateway: "sepa",
})
op = append(op, Operation{
Debtor: &Debtor{
IBAN: "test",
Name: "hey",
},
Creditor: &Creditor{
Name: "otr",
BIC: "hello",
},
Type: 2,
Gateway: "sepa",
})
res := conn.Create(op)
if res.Error != nil {
println(res.Error.Error())
}
Related
I have two models as follows:
type OHLCV struct {
gorm.Model
Interval string `gorm:"uniqueIndex:idx_ohlcv"`
Pair string `gorm:"uniqueIndex:idx_ohlcv"`
OpenTime time.Time `gorm:"uniqueIndex:idx_ohlcv"`
CloseTime time.Time `gorm:"uniqueIndex:idx_ohlcv"`
Open float64 `json:"open"`
High float64 `json:"high"`
Low float64 `json:"low"`
Close float64 `json:"close"`
Volume float64 `json:"volume"`
QuoteAssetVolume float64 `json:"quoteAssetVolume"`
NumberOfTrades float64 `json:"numberOfTrades"`
Calculations []Calculation `gorm:"foreignKey:OhlcvRefer"`
}
and
type Calculation struct {
gorm.Model
OhlcvRefer uint `gorm:"uniqueIndex:idx_calculation"`
Key string `gorm:"uniqueIndex:idx_calculation"`
Config string `gorm:"uniqueIndex:idx_calculation"`
Value float64
}
As you see both tables have unique indexes to prevent inserting duplicate data. The first table foreignKey is a part of the second table's unique index. The problem is how can I handle ON CONFLICT DO NOTHING behavior for both tables with a single GORM Create statement?
Before adding the Calculation association I was able handle CONFLICTS with
err = db.Clauses(clause.OnConflict{DoNothing: true,
Columns: []clause.Column{{Name: "interval"}, {Name: "pair"}, {Name: "open_time"}, {Name: "close_time"}},
}).Create(ohlcvs).Error
But now I get the following error:
ERROR: duplicate key value violates unique constraint "idx_calculation" (SQLSTATE 23505)
What I need is to DO NOTHING for the Calculation conflicts as well.
To achieve what you need, it should be enough to use the Unique index constraint on the two structs. Let's see how you can implement it.
package main
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type User struct {
Id int
Name string `gorm:"uniqueIndex:idx_name"`
Posts []Post
}
type Post struct {
Id int
Title string `gorm:"uniqueIndex:idx_title"`
UserId int
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Post{})
db.AutoMigrate(&User{})
db.Create(&User{
Name: "john doe",
Posts: []Post{
{Title: "first"},
{Title: "second"}, // to generate an error change to "first"
},
})
}
In this way, if you're entering duplicates value the db itself will block you. This is valid ono either on the users table and on the posts one. IMO, it's a very clean approach and you can be as flexible as you wish.
Let me know if this solves your issue or there is something else!
When I try to insert to a table with gorm which has one to many relationship, I get this error:
Error 1452: Cannot add or update a child row: a foreign key constraint fails (`Todo`.`todos`, CONSTRAINT `fk_users_todo_list` FOREIGN KEY (`fk_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE)....
These are my models:
type Base struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `gorm:"index"`
}
type User struct {
Base
FirstName string `form:"first_name" gorm:"type:varchar(20)"`
LastName string `form:"last_name" gorm:"type:varchar(20)"`
UserName string `form:"user_name" gorm:"unique;type:varchar(20)"`
Email string `form:"email" gorm:"type:varchar(100)"`
Password string `form:"password" gorm:"type:varchar(30)"`
Gender types.UserGender `form:"gender" gorm:"type:smallint"`
TodoList []Todo `gorm:"foreignKey:FkID;constraint:onDelete:SET NULL,onUpdate:CASCADE" json:"omitempty"`
}
type Todo struct {
Base
Name string
Description string
Status types.TaskStatus
FkID uint
}
And this is the function that I wrote:
func (d *DB) AddTODO(todo *models.Todo, userName string) error {
user := &models.User{UserName: userName}
err := d.db.Model(&user).Where("user_name = ?", userName).Association("TodoList").
Append([]models.Todo{*todo})
if err != nil {
return err
}
return nil
}
I'm using MariaDB.
It want user from gorm result. Change your method maybe like this:
// arg user is from gorm result somewhere
func (d *DB) AddTODO(user *models.User, todo *models.Todo) error {
return d.db.Model(user).Association("TodoList").Append([]models.Todo{*todo})
}
TodoList lack references keyword. It should be like this.
type User struct {
Base
FirstName string `form:"first_name" gorm:"type:varchar(20)"`
LastName string `form:"last_name" gorm:"type:varchar(20)"`
UserName string `form:"user_name" gorm:"unique;type:varchar(20)"`
Email string `form:"email" gorm:"type:varchar(100)"`
Password string `form:"password" gorm:"type:varchar(30)"`
Gender types.UserGender `form:"gender" gorm:"type:smallint"`
TodoList []Todo `gorm:"foreignKey:FkID;references:ID;constraint:onDelete:SET NULL,onUpdate:CASCADE" json:"omitempty"`
}
I have 2 models with Gorm like below:
type Post struct {
*gorm.Model
ID uint32 `gorm: "primarykey"`
PostTitle string `gorm:"posttitle;not null"`
PostSlug string `gorm:"postslug;unique;not null"`
PostCategoryID uint32 `gorm:"postcategoryid"`
PostCollections []PostCollection `gorm:"many2many:post_collection;"`
PostTags []PostTag `gorm:"many2many:post_tag; constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type PostCategory struct {
ID uint32 `gorm: "primarykey"`
PostCatName string `gorm:"postcatname;not null"`
PostCatSlug string `gorm:"postcatslug;unique;not null"`
PostCatParent uint32 `gorm: "postcatparent"`
Posts []Post `gorm:"foreignKey:PostCategoryID"`
}
I want get all posts of a parent category (which has many child categories) with function like below:
func GetPostByCategory(c *fiber.Ctx) error {
db := database.DB //connect database
var post []models.Post
var categories []models.PostCategory
cat := c.Params("catslug") //get slug value of parent category from route
cate := GetCategoryChild(cat) //get array of child category
db.Model(&categories).Where("id IN (?)", cate).Association("Posts").Find(&post)
return c.JSON(post)
}
It return nil array . Please help me to fix this
Problem
It is in your db.Model(&categories), you use an empty array of PostCategory. This will result in this query:
Code:
DB.Model(&categories).Where("id IN (?)", []int{1, 2, 3}).Association("Posts").Find(&post)
Output:
SELECT * FROM `posts` WHERE id IN (1,2,3) AND `posts`.`post_category_id` IN (NULL) AND `posts`.`deleted_at` IS NULL
The "id IN (?)", cate id is not the id of category, but the id of the post instead, because we are using Post type inside the .Find(&post)
You can try to apply logging into Gorm by using this config
databaseConfig := fmt.Sprintf("%s:%s#tcp(%s:%s)/%s?multiStatements=true&parseTime=true", "root", "", "127.0.0.1", "3306", "tester")
DB, _ = gorm.Open(mysql.Open(databaseConfig), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
Solution
Try to insert some data inside your categories
This is My Full Code, I comment several line of your code because no full code was provided.
// https://stackoverflow.com/questions/69977516/unsupported-relations-for-schema-with-has-many-relation-in-gorm
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
type Post struct {
*gorm.Model
ID uint32 `gorm:"primarykey"`
PostTitle string `gorm:"posttitle;not null"`
PostSlug string `gorm:"postslug;unique;not null"`
PostCategoryID uint32 `gorm:"postcategoryid"`
// PostCollections []PostCollection `gorm:"many2many:post_collection;"`
// PostTags []PostTag `gorm:"many2many:post_tag; constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type PostCategory struct {
ID uint32 `gorm:"primarykey"`
PostCatName string `gorm:"postcatname;not null"`
PostCatSlug string `gorm:"postcatslug;unique;not null"`
PostCatParent uint32 `gorm:"postcatparent"`
Posts []Post `gorm:"foreignKey:PostCategoryID"`
}
var DB *gorm.DB
func main() {
databaseConfig := fmt.Sprintf("%s:%s#tcp(%s:%s)/%s?multiStatements=true&parseTime=true", "root", "", "127.0.0.1", "3306", "tester")
DB, _ = gorm.Open(mysql.Open(databaseConfig), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
sqlDB, _ := DB.DB()
defer sqlDB.Close()
DB.AutoMigrate(&PostCategory{}, &Post{})
// ============ Function Get ================
var categories []PostCategory = []PostCategory{
{
ID: 1,
},
{
ID: 2,
},
}
var post []Post
DB.Model(&categories).Association("Posts").Find(&post)
fmt.Printf("%+v\n", post)
}
Output:
2021/11/23 20:28:41 D:/go/src/udemy-solving/00002/main.go:54
[1.054ms] [rows:3] SELECT * FROM `posts` WHERE id IN (1,2,3) AND `posts`.`post_category_id` IN (1,2) AND `posts`.`deleted_at` IS NULL
[{Model:0xc0001b5380 ID:1 PostTitle:example title 1 PostSlug:ex-1 PostCategoryID:1} {Model:0xc0001b53e0 ID:2 PostTitle:example title 2 PostSlug:ex-2 PostCategoryID:2} {Model:0xc0001b5440 ID:3 PostTitle:example title 3 PostSlug:ex-3 PostCategoryID:1}]
My MySQL Example Data:
post_categories
posts
Cassandra table and UDT
CREATE TYPE IF NOT EXISTS phone_type(
code TEXT,
phone TEXT,
);
CREATE TABLE IF NOT EXISTS user_by_phone(
user_id UUID,
phone FROZEN<phone_type>,
password TEXT,
PRIMARY KEY (phone)
);
golang struct
type PhoneType struct {
Code string `json:"code"`
Phone string `json:"phone"`
}
phone := PhoneType{
Code: "+1",
Phone: "7777777777",
}
gqcql query
err := Session.Query("SELECT user_id, password FROM user_by_phone WHERE phone=?;", phone).Scan(
&user.UserID,
&user.Password,
)
With this code it returns not found eventhough have a record in cassandra table.
How to query by UDT using gocql?
Try defining your struct like this (use cql instead of json in your struct definition)
type PhoneType struct {
Code string `cql:"code"`
Phone string `cql:"phone"`
}
phone := PhoneType{
Code: "+1",
Phone: "7777777777",
}
as per the example here:
https://github.com/gocql/gocql/blob/master/udt_test.go
This then worked for me:
var id gocql.UUID
var password string
iter := session.Query("SELECT user_id, password FROM user_by_phone WHERE phone=?;", phone).Iter()
for iter.Scan(&id, &password) {
fmt.Println("Phone Record:", id, password)
}
if err := iter.Close(); err != nil {
log.Fatal(err)
}
My two models are
package models
// Business ...
type Business struct {
ID uint
Name string `gorm:"not null"`
Tables Tables `gorm:"ForeignKey:BusinessID"`
}
// Businesses ...
type Businesses []Business
and
package models
// Table ...
type Table struct {
ID uint
Ref string `gorm:"not null"`
Business Business
BusinessID uint
}
// Tables ...
type Tables []Table
It may be obvious from the code, but the association should be that one 'business' has many 'tables' and a 'table' belong to a 'business'. However, when the database is created there are no foreign keys created (I am using sqlite3) and when I return the business which has been created with
bus := models.Business{
Name: "Test",
Tables: models.Tables{
models.Table{Ref: "A1"},
},
}
db.Create(&bus)
the businesses array is empty, and when the table is returned although the business_id is correct, there is business struct is empty also.
I could not reproduce your problem. I have a working solution here. I suspected that it would not work with the entities in a separate models package, but that worked as well.
package main
import (
"log"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
_ "github.com/mattn/go-sqlite3"
)
type Business struct {
ID uint
Name string `gorm:"not null"`
Tables Tables `gorm:"ForeignKey:BusinessID"`
}
type Table struct {
ID uint
Ref string `gorm:"not null"`
Business Business
BusinessID uint
}
type Tables []Table
type Businesses []Business
func main() {
var err error
var db *gorm.DB
db, err = gorm.Open("sqlite3", "test.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.LogMode(true)
db.AutoMigrate(&Business{})
db.AutoMigrate(&Table{})
bus := Business{
Name: "Test",
Tables: Tables{
Table{Ref: "A1"},
},
}
db.Create(&bus)
var businesses Businesses
db.Preload("Tables").Find(&businesses)
log.Println(businesses)
}