diff --git a/executors_test.go b/executors_test.go index 3202bfad0..db00a4284 100644 --- a/executors_test.go +++ b/executors_test.go @@ -1193,7 +1193,7 @@ func Test_Eager_Creation_Without_Associations(t *testing.T) { transaction(func(tx *Connection) { r := require.New(t) code := CourseCode{ - Course: Course{}, + Course: &Course{}, } err := tx.Eager().Create(&code) diff --git a/pop_test.go b/pop_test.go index fd2eb020a..417c3e4b0 100644 --- a/pop_test.go +++ b/pop_test.go @@ -291,14 +291,30 @@ type Course struct { } type CourseCode struct { - ID uuid.UUID `json:"id" db:"id"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt time.Time `json:"updated_at" db:"updated_at"` - CourseID uuid.UUID `json:"course_id" db:"course_id"` - Course Course `json:"-" belongs_to:"course"` + ID uuid.UUID `json:"id" db:"id"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + CourseID uuid.NullUUID `json:"course_id" db:"course_id"` + Course *Course `json:"course" belongs_to:"course" fk_id:"CourseID"` + ClassID uuid.UUID `json:"class_id" db:"class_id"` // Course Course `belongs_to:"course"` } +type Teacher struct { + ID uuid.UUID `json:"id" db:"id"` + Name string `json:"name" db:"name"` + CurrentClass *Class `json:"class" has_one:"class" fk_id:"current_teacher_id"` + Classes []Class `json:"classes" has_many:"class" fk_id:"teacher_id"` +} + +type Class struct { + ID uuid.UUID `json:"id" db:"id"` + Topic string `json:"topic" db:"topic"` + TeacherID uuid.NullUUID `json:"teachers_id" db:"teacher_id"` + CurrentTeacherID uuid.UUID `json:"current_teachers_id" db:"current_teacher_id"` + CourseCodes []CourseCode `json:"course_code_id" has_many:"course_codes"` +} + type ValidatableCar struct { ID int64 `db:"id"` Name string `db:"name"` diff --git a/preload_associations.go b/preload_associations.go index d29856674..75323a472 100644 --- a/preload_associations.go +++ b/preload_associations.go @@ -360,7 +360,9 @@ func preloadBelongsTo(tx *Connection, asoc *AssociationMetaInfo, mmi *ModelMetaI fkids := []interface{}{} mmi.iterate(func(val reflect.Value) { if !isFieldNilPtr(val, fi) { - fkids = append(fkids, mmi.mapper.FieldByName(val, fi.Path).Interface()) + if realValue := mmi.mapper.FieldByName(val, fi.Path).Interface(); !IsZeroOfUnderlyingType(realValue) { + fkids = append(fkids, realValue) + } } }) @@ -376,7 +378,8 @@ func preloadBelongsTo(tx *Connection, asoc *AssociationMetaInfo, mmi *ModelMetaI q.eagerFields = []string{} slice := asoc.toSlice() - err := q.Where(fmt.Sprintf("%s in (?)", fk), fkids).All(slice.Interface()) + vals := slice.Interface() + err := q.Where(fmt.Sprintf("%s in (?)", fk), fkids).All(vals) if err != nil { return err } diff --git a/preload_associations_test.go b/preload_associations_test.go index 933ef0694..f03d5a398 100644 --- a/preload_associations_test.go +++ b/preload_associations_test.go @@ -1,10 +1,10 @@ package pop import ( - "testing" - "github.com/gobuffalo/nulls" + "github.com/gofrs/uuid" "github.com/stretchr/testify/require" + "testing" ) func Test_New_Implementation_For_Nplus1(t *testing.T) { @@ -82,7 +82,7 @@ func Test_New_Implementation_For_Nplus1_With_UUID(t *testing.T) { courses = append(courses, course) if i == 0 { a.NoError(tx.Create(&CourseCode{ - CourseID: course.ID, + CourseID: uuid.NullUUID{UUID: course.ID, Valid: true}, })) } } @@ -113,6 +113,42 @@ func Test_New_Implementation_For_Nplus1_With_UUID(t *testing.T) { }) } +func Test_New_Implementation_For_Nplus1_With_NullUUIDs_And_FK_ID(t *testing.T) { + if PDB == nil { + t.Skip("skipping integration tests") + } + transaction(func(tx *Connection) { + teacher := &Teacher{ + Name: "Alice", + Class: &Class{ + Topic: "Is math related to science?", + CourseCodes: []CourseCode{{}}, + }, + } + + a := require.New(t) + + a.NoError(tx.Create(teacher)) + for k, cc := range teacher.Class.CourseCodes { + if cc.Course != nil { + a.NoError(tx.Create(cc.Course)) + teacher.Class.CourseCodes[k].CourseID = uuid.NullUUID{Valid: true, UUID: cc.Course.ID} + } + } + teacher.Class.TeacherID = uuid.NullUUID{UUID: teacher.ID, Valid: true} + a.NoError(tx.Eager().Create(teacher.Class)) + + var alice Teacher + a.NoError(tx.EagerPreload( + "Class.CourseCodes.Course", + ).First(&alice)) + + aliceClass := alice.Class + a.Len(aliceClass.CourseCodes, 1) + a.Nil(aliceClass.CourseCodes[0].Course) + }) +} + func Test_New_Implementation_For_Nplus1_Single(t *testing.T) { if PDB == nil { t.Skip("skipping integration tests") diff --git a/testdata/migrations/20181104140606_course_codes.up.fizz b/testdata/migrations/20181104140606_course_codes.up.fizz index f5d811655..7cbda2b5e 100644 --- a/testdata/migrations/20181104140606_course_codes.up.fizz +++ b/testdata/migrations/20181104140606_course_codes.up.fizz @@ -1,5 +1,6 @@ create_table("course_codes") { t.Column("id", "uuid", {"primary": true}) - t.Column("course_id", "uuid", {}) + t.Column("course_id", "uuid", {"null":true}) + t.Column("class_id", "uuid") t.Timestamps() -} \ No newline at end of file +} diff --git a/testdata/migrations/20210104145902_teachers.down.fizz b/testdata/migrations/20210104145902_teachers.down.fizz new file mode 100644 index 000000000..322c313c5 --- /dev/null +++ b/testdata/migrations/20210104145902_teachers.down.fizz @@ -0,0 +1 @@ +drop_table('teachers') diff --git a/testdata/migrations/20210104145902_teachers.up.fizz b/testdata/migrations/20210104145902_teachers.up.fizz new file mode 100644 index 000000000..dd6db4a5b --- /dev/null +++ b/testdata/migrations/20210104145902_teachers.up.fizz @@ -0,0 +1,13 @@ +create_table("teachers") { + t.Column("id", "uuid", {"primary": true}) + t.Column("name", "string") + t.DisableTimestamps() +} + +create_table("classes") { + t.Column("id", "uuid", {"primary": true}) + t.Column("topic", "string") + t.Column("teacher_id", "uuid", {"null": true}) + t.ForeignKey("teacher_id", {"teachers": ["id"]}, {}) + t.DisableTimestamps() +}