bun icon indicating copy to clipboard operation
bun copied to clipboard

Has many self relationship throwing error

Open garthreckers opened this issue 2 years ago • 4 comments

This may be a similar to this issue but when I try to do a has-many relationship that references the same model, it won't work if the parent field is a pointer (edit: used to say non-pointer but should have said pointer).

This is the non-working code:

type (
	Category struct {
		bun.BaseModel `bun:"category,alias:category"`

		ID          uint        `bun:"id,pk,autoincrement" json:"id"`
		Name        string      `bun:"name" json:"name"`
		ParentID    *uint       `bun:"parent" json:"parent_id"`
		CreatedAt   *time.Time  `bun:"created_at" json:"created_at"`
		UpdatedAt   *time.Time  `bun:"updated_at" json:"updated_at"`
		AllChildren []*Category `bun:"rel:has-many,join:id=parent" json:"all_children,omitempty"`
	}
)

func ListCategories() []Category {
	var categories []Category

	db.GetDB().
		NewSelect().
		Model(&categories).
		Relation("AllChildren").
		Where("parent IS NULL").
		Order("name ASC").
		Scan(context.TODO())

	return categories
}

The above code will return the parent categories without the children. If I check the logs, I see this error:

SELECT `category`.`id`, `category`.`name`, `category`.`parent`, `category`.`created_at`, `category`.`updated_at` FROM `category` WHERE (`category`.`parent` IN (4, 7, 15, 1))      *errors.errorString: bun: has-many relation=AllChildren does not have base model=Category with id=[%!q(*uint=0x400035ab58)] (check join conditions)

If I replace the ParentID with a non-pointer uint and run the code, it works as expected with the parents at the top level and the nested AllChildren.

type (
	Category struct {
		bun.BaseModel `bun:"category,alias:category"`

		ID          uint        `bun:"id,pk,autoincrement" json:"id"`
		Name        string      `bun:"name" json:"name"`
		ParentID    uint        `bun:"parent" json:"parent_id"`
		CreatedAt   *time.Time  `bun:"created_at" json:"created_at"`
		UpdatedAt   *time.Time  `bun:"updated_at" json:"updated_at"`
		AllChildren []*Category `bun:"rel:has-many,join:id=parent" json:"all_children,omitempty"`
	}
)

func ListCategories() []Category {
	var categories []Category

	db.GetDB().
		NewSelect().
		Model(&categories).
		Relation("AllChildren").
		Where("parent IS NULL").
		Order("name ASC").
		Scan(context.TODO())

	return categories
}

Am I missing something?

I tried the duplicate struct that the above issue (they did Message and MessageX) and could get it to work that way but that's not really ideal.

garthreckers avatar Aug 18 '23 21:08 garthreckers

@codeliger - Doesn't look like its necessary for a relationship. I have a lot of other working relationships without the leading comma and the documentation doesn't include it: https://bun.uptrace.dev/guide/relations.html#has-many-relation

Thanks for the suggestion though.

garthreckers avatar Aug 26 '23 15:08 garthreckers

Have you tried renaming AllChildren to Categories, or specifying a table name in the definition?

I think the reason there is no table name defined is because it derives it from Profiles []*Profile `bun:"rel:has-many,join:id=user_id"

codeliger avatar Aug 28 '23 13:08 codeliger

@codeliger - I tried changing it to Categories but it throws the same error:

*errors.errorString: bun: has-many relation=Categories does not have base model=Category with id=[%!q(*uint=0x40004397c0)] (check join conditions)

When you say specify a table name, where would that be added? I have a table definition in the BaseModel.

From my debugging, it makes the correct database query since I can see the relationship query when I run it in debug mode:

SELECT `category`.`id`, `category`.`name`, `category`.`parent`, `category`.`created_at`, `category`.`updated_at` FROM `category` WHERE (`category`.`parent` IN (1, 2)) 

The issue I believe is when it's trying to attach the relationship query result to the base (parent) query result struct, it's not handling the pointer properly. I'm not sure if doing a comparison between a memory address and the value or there is some other issue somewhere where it should get de-referenced but its not.

garthreckers avatar Aug 30 '23 16:08 garthreckers

Has anyone found a solution? I have the same problem :/

taoberquer avatar Apr 17 '24 12:04 taoberquer