ex_machina icon indicating copy to clipboard operation
ex_machina copied to clipboard

Naively inserting a record with children doesn't work

Open bencates opened this issue 8 years ago • 3 comments

First, thanks for the awesome project!

I'm working on an app that has two Ecto schemas, Order and Product. Orders have many products, and products belong to orders. I have the following factories:

def order_factory() do
  %MyApp.Order{
    # snip
  }
end

def product_factory() do
  %MyApp.Product{
    # snip
    order: build(:order),
  }
end

In my tests I attempt to insert an order and a list of products like so:

order = insert(:order, products: [build(:product)])

and I get the somewhat cryptic error "** (ArgumentError) cannot change belongs_to association order because there is already a change setting its foreign key order_id to 3305".

This works, though:

order = insert(:order, products: [build(:product, order: nil)])

It's not a terribly difficult workaround, but it adds a significant amount of irrelevant noise to my tests, especially on tests which require deeply nested objects.

bencates avatar Jun 01 '17 17:06 bencates

Thank you @bencates for posting this workaround! My current solution was to insert order first and then product which was even more noisy. It would be great if inserting list in has_many association worked out of the box. If I have some time this week, I'll try to familiarize with ex_machina internals and check how much time it would get me to fix the issue.

tomekowal avatar Jun 12 '17 10:06 tomekowal

Hi! I don't have full solution yet, but I have found a way to reproduce the problem. ex_machina has a test for has_many association here: https://github.com/thoughtbot/ex_machina/blob/master/test/ex_machina/ecto_strategy_test.exs#L55 but it passes only by accident.

In model definitions:

schema "users" do
  ...
  has_many :articles, ExMachina.Article,
  ...
end

and:

schema "articles" do
  ...
  belongs_to :author, ExMachina.User
  ...
end

has_many association will search user_id in Article, but Article defines author_id, so in the test when we do:

built_article = TestFactory.build(:article, visits: 10)
model = TestFactory.insert(:user, articles: [built_article])

there are actually two users created instead of one. If we fix the schema by referencing correct foreign key:

schema "users" do
  ...
  has_many :articles, ExMachina.Article, foreign_key: :author_id,
  ...
end

we will get the same error as @bencates posted above. I've made clone with branch that demonstrates the problem (two tests don't pass after fixing the schema), but I don't think it makes sense to turn it into a PR before I have a fix. https://github.com/tomekowal/ex_machina/tree/fix-user-has-many-posts-association

tomekowal avatar Jun 14 '17 01:06 tomekowal

I've submitted a PR that fixes both tests and implementation. I hope it proves useful :)

tomekowal avatar Jun 14 '17 12:06 tomekowal