-
Notifications
You must be signed in to change notification settings - Fork 81
Open
Labels
questionFurther information is requestedFurther information is requested
Description
The factory generates a new parent entity for every associated child, causing many parent records to be created. To illustrate the issue, imagine we have this schema:
CREATE TABLE orders
(
id SERIAL PRIMARY KEY
);
CREATE TABLE order_items
(
id SERIAL PRIMARY KEY,
order_id INTEGER NOT NULL REFERENCES orders (id)
);and this config:
psql:
driver: github.com/jackc/pgx/v5/stdlib
type_system: database/sql
output: test/db
pkgname: dbtest
schemas:
- public
uuid_pkg: gofrsWhen I try to create an order with 2 order items, the factory inserts a new order for every child item, so 3 orders are created in total.
func TestChild(t *testing.T) {
f := factory.New()
// orders should be created with 2 items
f.AddBaseOrderMod(
factory.OrderMods.WithNewOrderItems(2),
)
// doesn't do anything
f.AddBaseOrderItemMod(
factory.OrderItemMods.WithoutOrder(),
)
db := must(sql.Open("postgres", "dbname=bob sslmode=disable"))
bobDB := bob.NewDB(db)
ctx := context.Background()
withRollback(ctx, bobDB, func(tx bob.Executor) error {
_, err := f.NewOrder().Create(ctx, tx)
if err != nil {
return err
}
total, err := models.Orders.Query().Count(ctx, tx)
if err != nil {
return err
}
t.Log(total) // prints 3, expected 1
return nil
})
}
func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
func withRollback(ctx context.Context, db bob.DB, fn func(tx bob.Executor) error) {
tx := must(db.BeginTx(ctx, nil))
defer tx.Rollback(ctx)
exec := bob.Debug(tx)
if err := fn(exec); err != nil {
panic(err)
}
}test output
=== RUN TestChild
INSERT INTO "orders" AS "orders"("id")
VALUES (DEFAULT)
RETURNING "orders"."id" AS "id"
INSERT INTO "orders" AS "orders"("id")
VALUES (DEFAULT)
RETURNING "orders"."id" AS "id"
INSERT INTO "order_items" AS "order_items"("id", "order_id")
VALUES (DEFAULT, $1)
RETURNING "order_items"."id" AS "id", "order_items"."order_id" AS "order_id"
0: int32: 17
INSERT INTO "orders" AS "orders"("id")
VALUES (DEFAULT)
RETURNING "orders"."id" AS "id"
INSERT INTO "order_items" AS "order_items"("id", "order_id")
VALUES (DEFAULT, $1)
RETURNING "order_items"."id" AS "id", "order_items"."order_id" AS "order_id"
0: int32: 18
UPDATE "order_items" AS "order_items" SET
"order_id" = $1
WHERE ("order_items"."id" IN ($2, $3))
RETURNING "order_items"."id" AS "id", "order_items"."order_id" AS "order_id"
0: omit.Val[int32]: 16
1: int32: 11
2: int32: 12
SELECT
count(1)
FROM "orders" AS "orders"
LIMIT 1
OFFSET 0
db_test.go:55: 3
--- PASS: TestChild (0.04s)
The issue seems to be caused by this bit.
bob/gen/templates/factory/table/03_create.go.tpl
Lines 117 to 118 in 0820242
| if o.r.{{$relAlias}} == nil { | |
| {{$tAlias.UpSingular}}Mods.WithNew{{$relAlias}}().Apply(ctx, o) |
Adding .OrderItemMods.WithoutOrder() doesn't do anything, because the generated code looks like this, which always creates a parent even though we told it not to.
func (m orderItemMods) WithoutOrder() OrderItemMod {
return OrderItemModFunc(func(ctx context.Context, o *OrderItemTemplate) {
o.r.Order = nil
})
}
func (o *OrderItemTemplate) Create(ctx context.Context, exec bob.Executor) (*models.OrderItem, error) {
var err error
opt := o.BuildSetter()
ensureCreatableOrderItem(opt)
if o.r.Order == nil {
OrderItemMods.WithNewOrder().Apply(ctx, o)
}Metadata
Metadata
Assignees
Labels
questionFurther information is requestedFurther information is requested