GORM Playground Link

https://github.com/go-gorm/playground/pull/459

Description

In this sample, I have a model Article with the field code that is indexed with a unique index. An article has many article names. So I have another model ArticleName with the foreign key to the article table article_id, a field name that is indexed with a unique index (the unique indices in this sample, are useful to perform upsert operations).

I want to ask if it is possible to upsert articles and article names as follows:

// Models

type Article struct {
    ID           uint64
    Code         string `gorm:"index:idx_code,unique"`
    ArticleNames []*ArticleName
}

type ArticleName struct {
    ID        uint64
    ArticleID uint64
    Name      string `gorm:"index:idx_name,unique"`
    Language  string
    Article   *Article
}

// Creation

articles := []*Article{
        {
            Code: "123",
            ArticleNames: []*ArticleName{
                {Name: "Jacket", Language: "en"},
                {Name: "Giacca", Language: "it"},
            },
        },
        {
            Code: "456",
            ArticleNames: []*ArticleName{
                {Name: "Shirt", Language: "en"},
                {Name: "Maglietta", Language: "it"},
            },
        },
    }

DB.Clauses(clause.OnConflict{UpdateAll: true}).
        Session(&gorm.Session{FullSaveAssociations: true}).
        Create(&articles)

// Update (case no changes)

articles = []*Article{
        {
            Code: "123",
            ArticleNames: []*ArticleName{
                {Name: "Jacket", Language: "en"},
                {Name: "Giacca", Language: "it"},
            },
        },
        {
            Code: "456",
            ArticleNames: []*ArticleName{
                {Name: "Shirt", Language: "en"},
                {Name: "Maglietta", Language: "it"},
            },
        },
    }

DB.Clauses(clause.OnConflict{UpdateAll: true}).
        Session(&gorm.Session{FullSaveAssociations: true}).
        Create(&articles)

// Update (case with changes)

articles = []*Article{
        {
            Code: "123",
            ArticleNames: []*ArticleName{
                {Name: "Jacket", Language: "en"},
                {Name: "Giacca", Language: "it"},
            },
        },
        {
            Code: "456bis",
            ArticleNames: []*ArticleName{
                {Name: "T-Shirt", Language: "en"},
                {Name: "Maglietta", Language: "it"},
            },
        },
    }

DB.Clauses(clause.OnConflict{UpdateAll: true}).
        Session(&gorm.Session{FullSaveAssociations: true}).
        Create(&articles)

Now the first DB.Create works and creates the 2 articles with names as expected. 🤟 The second DB.Create returns the following error:

Error 1452: Cannot add or update a child row: a foreign key constraint fails (`gorm`.`article_names`, CONSTRAINT `fk_articles_article_names` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`))
[5.139ms] [rows:0] INSERT INTO `article_names` (`article_id`,`name`,`language`) VALUES (0,'Jacket','en'),(0,'Giacca','it'),(0,'Shirt','en'),(0,'Maglietta','it') ON DUPLICATE KEY UPDATE `article_id`=VALUES(`article_id`),`name`=VALUES(`name`),`language`=VALUES(`language`)

As you can see, the article_id value is set to 0. How to avoid this?

Then the third DB.Create returns the following error:

Error 1452: Cannot add or update a child row: a foreign key constraint fails (`gorm`.`article_names`, CONSTRAINT `fk_articles_article_names` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`))
[8.834ms] [rows:0] INSERT INTO `article_names` (`article_id`,`name`,`language`) VALUES (5,'Jacket','en'),(5,'Giacca','it'),(6,'T-Shirt','en'),(6,'Maglietta','it') ON DUPLICATE KEY UPDATE `article_id`=VALUES(`article_id`),`name`=VALUES(`name`),`language`=VALUES(`language`)

In this case, article_id are set to 4 and 5, but these articles do not exist.

Do you know how to pass the test I wrote in the linked playground?

@jinzhu heeeeeeeeeelppppppp 🙏

Comment From: tommaso1

+1

Comment From: Manik5

+1

Comment From: jinxuanzheng01

+1

Comment From: hewenyu

这个upsert只能根据主键来吧,你的操作对程序来说会新生成一个 主键id,但是由于你设置了唯一字段,无法成功插入,所以中间作为链接的表的外键值也不存在了,你把unique 去掉,理论上应该就行了

This upsert can only be based on the primary key. Your operation will generate a new primary key ID for the program, but because you set a unique field, it cannot be successfully inserted, so the foreign key value of the linked table in the middle does not exist. If you remove unique, it should be OK in theory