Saving relationships

34 views
Skip to first unread message

Andre Nathan

unread,
May 12, 2011, 2:52:48 PM5/12/11
to chica...@googlegroups.com
Hello

Say I have a model with multiple has_many relationships. For example,

-module(bookstore, [Id, Name]).
-has({employees, many}).
-has({books, many}).

Now say that when I create a new bookstore, the employes and books are
inputs in the bookstore form, so that in my controller I actually have
to save instances of the 3 models. I also want to abort the whole
process if any of those fail validation.

I'm doing something like this:

- Create a new bookstore
- Create the employees, with bookstore_id undefined
- Create the books, with bookstore_id undefined
- Call validate() on all those instances; if any of them fails, abort
- Save the bookstore
- Assign the bookstore ID to each employee and book
- Save the employees and books

The code for this can become somewhat complicated as more relationships
are added (not to mention really boring to write), and it seems wasteful
too because I have to call validate() then save(), which calls
validate() itself.

So, is there a better way do this? It would be cool if there was a way
to set related objects directly, something like

B1 = bookstore:new(id, "foo"),
B2 = B1:employees(ListOfEmployees),
B3 = B2:books(ListOfBooks),
B3:save() % validates and saves each associated object too

Is it possible to add something like this to the API?

Thanks,
Andre

Evan Miller

unread,
May 15, 2011, 3:28:03 PM5/15/11
to chica...@googlegroups.com
Hi Andre,

The trouble with this approach is that the Bookstore object is really
just a value-tuple and never includes any information about associated
books or employees; these are fetched from the database each time
employees() or books() is called. There's not a clean way that comes
to mind for keeping track of associated non-saved objects.

However, your problem might be better solved with database
transactions.... maybe the API would look like

ok = boss_db:transaction(fun() ->
Bookstore = bookstore:new(id, "foo"),
{ok, SavedBookstore} = Bookstore:save(),

lists:map(fun(Employee) ->
E1 = Employee:bookstore_id(SavedBookstore:id()),
{ok, E2} = E1:save()
end, ListOfEmployees),

lists:map(fun(Book) ->
...
end, ListOfBooks)
end)


Any run-time error would result in the transaction being rolled back.
What do you think?

Not all databases support transactions but we could probably use the
transaction semantics in Mnesia, MySQL, and Postgres at the very
least.

>
> Thanks,
> Andre
>
>

--
Evan Miller
http://www.evanmiller.org/

Andre Nathan

unread,
May 15, 2011, 10:38:15 PM5/15/11
to chica...@googlegroups.com
On Sun, 2011-05-15 at 14:28 -0500, Evan Miller wrote:
> ok = boss_db:transaction(fun() ->
> Bookstore = bookstore:new(id, "foo"),
> {ok, SavedBookstore} = Bookstore:save(),
>
> lists:map(fun(Employee) ->
> E1 = Employee:bookstore_id(SavedBookstore:id()),
> {ok, E2} = E1:save()
> end, ListOfEmployees),
>
> lists:map(fun(Book) ->
> ...
> end, ListOfBooks)
> end)
>
> Any run-time error would result in the transaction being rolled back.
> What do you think?

That would be great! It would make it much easier to write higher level
functions that handle saving an object and its associations without
having to validate them all first.

Andre

Graeme Defty

unread,
May 15, 2011, 11:22:41 PM5/15/11
to chica...@googlegroups.com
Evan,

Looks good to me.

g
________________________________

Evan Miller

unread,
May 15, 2011, 11:31:01 PM5/15/11
to chica...@googlegroups.com
Cool. Try out this patch, which should work with both the Mnesia and
MySQL adapters.

The API is

boss_db:transaction(TransactionFun::function()) -> {atomic,
ResultOfFun} | {aborted, Reason}

Hopefully adding Postgres support won't be too hard.

cb-transactions.diff

Evan Miller

unread,
May 16, 2011, 12:12:40 AM5/16/11
to chica...@googlegroups.com
OK went ahead and pushed this to GitHub along with transaction support
for Postgres and the in-memory DB. Let me know if you run into
trouble. Unfortunately MongoDB and Tyrant don't support transactions,
so choose your database carefully.

Evan

Reply all
Reply to author
Forward
0 new messages