Rails provides us with a lot of power in routing and associations, but if you’ve ever tried to set up an API with any form of many-to-many relationship, you’re in for a nightmare. Google won’t save you, the Rails guides are sparse, and there’s a grand total of one good blog post on the matter from a few years ago.
Many to Many
So how does a many to many relationship work? Via an association table containing IDs of both of the resources to be linked. Both then have access to the other collection. It’s extremely handy for certain problems, and if you’re just using Rails through the view you’ll likely never have a problem with it.
The Fun Starts
But now you’ve heard about this awesome thing called Angular / Ember / New Hot JS Framework that you just have to use. I don’t blame you, a few weeks in Angular and I don’t want to use Rails Views again. You decide to take the high road and segregate the apps, making Rails an API and using your framework (Angular assumed from here on out) to build out the frontend through calls.
It all works great, you even found RestAngular to help you out with some of the plumbing. Simple actions are now trivial. Want a list of a Users comments? Easy:
1 2 |
|
But then there are Categories
RestAngular already has us covered, any other case and we’re sailing along. Now we want to add categories to our posts, a many to many relationship. How would we script that one? Likely the first thing you try is this:
1 2 |
|
Checking the DB, you’ll notice the new association isn’t there. Odd. Maybe delete will work?
1 2 |
|
…except now for some reason, category one is gone everywhere. Thinking through it, it becomes clear that what we’ve done is simply request a nested resource and sent it a delete request.
So what do you do?
There’s an association table with your name on it called something like PostCategory. Trying to route through either one of the hosts is likely to give you nightmares.
First let’s take a look at what your controller action should look like to handle the queries:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Note that the index action is a very succinct way of saying:
1 2 3 4 5 |
|
Though the latter has been known to drive me to very lengthy discussions on mutability morality and ethics.
This allows us to search against either posts or categories depending on the params, but this can only work if we cheat a bit around the routes and define a DELETE
action on the root resource:
1
|
|
Not exactly the most straightforward method, but given the odd alternatives like adding controller actions to either of the ends of the relation like post#add_category
and adding multiple routes for every time you try it I far and prefer this idea. The only real difference is that you end up with a request like this instead:
1
|
|
Now all we have to do are basic actions like on any other service and we’re golden:
1 2 3 4 5 6 7 8 9 |
|
Wrap it in a service and you’re set to go. Just remember that the association tables are there for a reason, use them. Rely on too much rails magic and you’ll end up burned thinking something’s going to work.
I welcome any thoughts on how better to address such issues as this in the comments, I’d love to hear your opinions!