Question

Is it possible to create a reference without creation of association itself in many-to-many use case?

E.g. I have tables - users, languages and joint table user_languages. languages table is already fulfilled with a list of available languages. When I'm creating a user with languages I don't need to do insertion into languages but I want to create a reference in a joint table only. In gorm v1 such behaviour was achieved by using association_autoupdate:false tag in a model. In gorm v2 method Omit("Languages") also omits reference. Thank you!

Comment From: yzha5

gorm v2 However, even if Omit("Languages") is used, the INSERT INTO `languages`... statement will still be executed

type User struct {
    Id int
    Languages []Language `gorm:"many2many:language_user"`
}

type Language struct {
    Id int
}

When I run

db.Debug().Model(&User{Id: 1}).Omit("Languages").Association("Languages").Append([]Language{{Id: 9},{Id: 10}})

The console printed these two lines of statements

//INSERT INTO `languages` ...
//INSERT INTO `language_user` ...

INSERT INTO `languages` ... I don’t want this because I set the verification in the hook of Language

Comment From: wangeguo

I have the same question

Comment From: devhossamali

I believe this should be considered as a bug not feature_request

Comment From: jinzhu

The latest master support to use the following code to skip the associations itself

db.Omit("Languages.*")

Comment From: jinzhu

Updated the document https://gorm.io/docs/associations.html#Skip-Auto-Create-Update

Comment From: leefernandes

@jinzhu Is this in a tagged release? I came over from this linked issue - https://github.com/go-gorm/gorm/issues/3710

On 1.20.8 I'm still seeing INSERT INTO "languages" inserts when just wanting the association record in the association table to be created with provided IDs, and want gorm to please leave the resource tables alone when making just an m2m association between two existing records.

db.Debug().Model(&User{Id: 1}).Omit("Languages.*").Association("Languages").Append([]Language{{Id: 9},{Id: 10}})

I'm looking for an api method to ONLY create the language_user association table record.

Actual

INSERT INTO "languages"...

INSERT INTO "language_user"...

Expected (Desired?)

INSERT INTO "language_user"...

Comment From: knlambert

@ItsLeeOwen same issue for me. it's easy to find a workaround, but it's misleading since this was working in the previous version.

Comment From: GiancarlosIO

👀

Comment From: inliquid

@jinzhu any updates on last questions?

Comment From: birdycn

同样的问题,append不希望创建,因为忽略了部分非空值,导致append失败

Comment From: devhossamali

This bug is now resolved, please upgrade to v1.21.4 or later releases

Comment From: ngocketit

It's still an issue for me. Such a huge disappointment with Gorm that it can't even handle such a basic use-case

Comment From: ngocketit

I tried all different ways mentioned in the docs but nothing worked. The use case is to add existing records to the many-to-many relationship. Had to resort to raw queries as I didn't want to waste more time on this:

return svc.db.Transaction(func(tx *gorm.DB) error {
    for _, userId := range request.Instructors {
        if err := tx.Exec("INSERT INTO program_instructors VALUES (?, ?)", program.ID, userId).Error; err != nil {
            return err
        }
    }
    return nil
})

Comment From: jinzhu

It's still an issue for me. Such a huge disappointment with Gorm that it can't even handle such a basic use-case

we have tests & users confirmed that works, if it doesn't work for you, create an issue with reproducible PR.

Comment From: tolik505

@ngocketit I'm using v1.21.16 and tx.Omit("Languages.*") works as expected for me. It creates relations in joint table without inserting new records into languages table. Please, check gorm version in your project.

Comment From: ngocketit

It's still an issue for me. Such a huge disappointment with Gorm that it can't even handle such a basic use-case

we have tests & users confirmed that works, if it doesn't work for you, create an issue with reproducible PR.

Thanks for your comment @jinzhu. Maybe I missed something but here is the code I used:

var users []*models.User
if err := svc.db.Where("id IN ? and school_id = ?", request.Instructors, program.SchoolID).Find(&users).Error; err != nil {
    return err
}
instructors, err := svc.GetInstructors(program)
if err != nil {
    return err
}
instructors = append(instructors, users...)
return svc.db.Model(program).Omit("Instructors").Association("Instructors").Replace(instructors)

That will result in the users being inserted in users table. As the users already exist, it shouldn't be that way. Then I tried Append instead of Replace:

var users []*models.User
if err := svc.db.Where("id IN ? and school_id = ?", request.Instructors, program.SchoolID).Find(&users).Error; err != nil {
    return err
}
return svc.db.Model(program).Omit("Instructors").Association("Instructors").Append(users)

And I got:

reflect: call of reflect.Append on zero Value
/usr/local/go/src/reflect/value.go:221 (0x10ccbe4)
    flag.mustBe: panic(&ValueError{methodName(), f.kind()})
/usr/local/go/src/reflect/value.go:2048 (0x10cca0f)
    Append: s.mustBe(Slice)
/Users/Workspace/my/go/pkg/mod/gorm.io/gorm@v1.21.16/association.go:354 (0x14ffcbe)
    (*Association).saveAssociation.func1.1: fieldValue = reflect.Append(fieldValue, ev)
/Users/Workspace/my/go/pkg/mod/gorm.io/gorm@v1.21.16/association.go:369 (0x15003b3)
    (*Association).saveAssociation.func1: appendToFieldValues(reflect.Indirect(rv.Index(i)).Addr())
/Users/Workspace/my/go/pkg/mod/gorm.io/gorm@v1.21.16/association.go:469 (0x14e2521)
    (*Association).saveAssociation: appendToRelations(reflectValue, rv, clear && idx == 0)
/Users/Workspace/my/go/pkg/mod/gorm.io/gorm@v1.21.16/association.go:58 (0x14dd4c4)
    (*Association).Append: association.saveAssociation( /*clear*/ false, values...)

Gorm version: v1.21.16

Comment From: ngocketit

@ngocketit I'm using v1.21.16 and tx.Omit("Languages.*") works as expected for me. It creates relations in joint table without inserting new records into languages table. Please, check gorm version in your project.

Thank you @tolik505 ! Using .Omit("Instructors.*") indeed works for Replace. However, I still get reflect: call of reflect.Append on zero Value with Append

Comment From: andrew-atwood-infinitusai

@ngocketit I was trying to do this same thing and got the same error, and eventually figured it out. Well, I figured out that specific error, I have yet to figure out how to skip the INSERT which is the whole thing, but it's a step forward.

The reflect: call of reflect.Append on zero Value error is due to your parent User model not having an existing slice on the Languages field. The Assocation.Append method tries to actually append the given entries to the parent model before doing anything with query building, as silly as it sounds.

I don't know when, outside of procedural example code, you would actually have that array on your User when doing a simple update by IDs, but alas!

Comment From: linktomarkdown

gorm v2 However, even if Omit("Languages") is used, the INSERT INTO `languages`... statement will still be executed

``go type User struct { Id int Languages []Languagegorm:"many2many:language_user"` }

type Language struct { Id int } ```

When I run

go db.Debug().Model(&User{Id: 1}).Omit("Languages").Association("Languages").Append([]Language{{Id: 9},{Id: 10}})

The console printed these two lines of statements

//INSERT INTO `languages` ... //INSERT INTO `language_user` ...

INSERT INTO `languages` ... I don’t want this because I set the verification in the hook of Language

thank you very much. you save my life.