Rails Active Record multiple associations - ruby-on-rails

I have a Rails app using Postgres and Active Record, and can't fathom the most efficient Associations between my models.
I have a Model called Article. Article needs to have a Format and 2/3 Genres.
I need articles to be able to be listed by format i.e. http://myapp.com/format/format-id/article-id
I also need articles to be listed by genre, so: myapp.com/genre/genre-name/article-id
I was thinking the Genres and Formats would be models themselves with has_many_and_belongs_to associations to Articles. Each article has multiple Genres, and 1 format, but each Genre has multiple articles.
I know this should be simple, but I can't find the most efficient route of making this happen.

There's an old Railscasts episode that goes through the two ways to do many-to-many associations with Rails - you should watch it: http://railscasts.com/episodes/47-two-many-to-many. It's old but mostly still relevant.
So, you've got Articles, Formats, and Genres. Since each Article will have only one Format, you don't need a has_and_belongs_to_many relationship there. Article belongs_to Format, and Format has_many articles.
Each row in the articles table will have a format_id field to indicate which Format it belongs to.
The relationship between Genres and Articles is a many-to-many, which is a bit trickier. At the database level, this requires a 'join table'. Your join table would be called something like articles_genres, and each row represents one genre that one particular article has. So, it'd have a genre_id column, and an article_id column.
In terms of rails, there are two ways to do this:
Give the articles_genres table it's own model. In this case you might want to also give the table a different name, to indicate it's a Model in it's own right, not just a join table. You could call it something like genreizations
or
Don't give articles_genres table it's own model, and let rails handle it all.
I generally prefer the first way - I feel more in control, and it leaves things more flexible for future. But, either will work. The railscasts episode I linked to describes both ways.
If you go the first way, this is what models you'll have:
class Article < ActiveRecord::Base
has_many :genreizations
has_many :genres, through: :genreizations
belongs_to :format
end
class Format < ActiveRecord::Base
has_many :articles
end
class Genreization < ActiveRecord::Base
belongs_to :article
belongs_to :genre
end
class Genre < ActiveRecord::Base
has_many :genreizations
has_many :articles, through: :genreizations
end
And in the second way, this is what models you'll have:
class Article < ActiveRecord::Base
has_and_belongs_to_many :genres
belongs_to :format
end
class Format < ActiveRecord::Base
has_many :articles
end
class Genre < ActiveRecord::Base
has_and_belongs_to_many :articles
end

Related

Voting for nested objects using Acts as Votable gem in a has_many through association

Using this answer Voting for nested objects using the Acts As Votable gem I was able to get voting working for my app but not exactly what I was hoping for. In Carl's example his "dish" model belongs_to :dish_category but my setup looks more like this:
class Dish < ActiveRecord::Base
has_many :restaurants, through: :dish_categories
end
Right now when someone votes for a dish, it counts as a vote for that dish regardless of the restaurant. I would like to figure out if it's possible for dishes to have separate voting counts based on current restaurant. So if I'm a user I can vote up pizza at one restaurant but then vote down pizza at a different restaurant.
So, after getting a bit more data together, I think I have a solution that, although seemingly complicated, it's simple really and organized.
I would suggest that you have your Restaurant and Dish models, as well as your DishRestaurant join table (or DishCategories as you have it in your original post), and finally a VoteContainer to hold the logic for votes.
class Restaurant < ApplicationRecord
has_many :dish_restaurants
has_many :dishes, through: :dish_restaurants
end
class Dish < ApplicationRecord
has_many :dish_restaurants
has_many :restaurants, through: :dish_restaurants
def votes
dish_restaurants.vote_container
end
end
class DishRestaurant < ApplicationRecord
belongs_to :dish
belongs_to :restaurant
has_one :vote_container
end
class VoteContainer < ApplicationRecord
act_as_votable
belongs_to :dish_restaurant
end
I was thinking of something along these lines may work. In theory, dish.votes should be your gateway to the logic for managing votes
Original Reply (disregard)
So, what I'm thinking is you may have what may feel like duplicate dishes. So, you'll create a new pizza dish for each restaurant that offers pizza. Technically it's not a duplicate, as it has it's own vote count for that particular restaurant. This would probably be the easiest and most sensible way from what you're currently presenting.
This makes sense also because not every restaurant makes pizza the same way as others. Each pizza may have different attributes (price, ingredients, etc). If you do it the way I've suggested, you'll be open to these p

Rails STI design issue

I am working a side project where a user can have multiple clients. Those client can be of type Person or Business.
I was leaning toward the idea to using STI but I am not sure whether this is the right way to go since my models will not share the same attributes.
For instance a Business has a legal_form where a Person might have a marital_status.
Is it ok to use STI in this particular case or (2nd question) is there any way to allow rails to use separate tables for each types.
STI is like inheritance in ruby. You can use it if you have parent and children and they share a lot of attributes and data. If Person and Business share a lot you can use it. Otherwise I'd recommend you use Polymorphic Associations
A slightly more advanced twist on associations is the polymorphic
association. With polymorphic associations, a model can belong to more
than one other model, on a single association. For example, you might
have a picture model that belongs to either an employee model or a
product model. Here's how this could be declared:
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
I don't really like STI and I'd recommend you try to use Polymorphic Associations.
A common problem with STI
A common problem with STI is that, over time, more types get added to
that table, and it grows more and more columns, and the records in the
table have less and less in common with one another. Each type of
record uses some subset of the table’s columns, and none uses all of
them, so you end up with a very sparsely populated table. Those types
create costs for one another: when you query for articles, you must
remember to filter out all the other types of values and to only
select columns relevant to articles, or else pay a huge performance
cost. You reproduce a lot of work the database would do for you, if
you only had each data type in its own table.
Assuming I would go for the polymorphic association would this be correct
class Client < ActiveRecord::Base
belongs_to :cliental, polymorphic: true
end
class Business < ActionRecord::Base
# Here I am using has_one because I do not want to have duplicates
has_one :client, as: :cliental
end
class Person < ActionRecord::Base
# Here I am using has_one because I do not want to have duplicates
has_one :client, as: :cliental
end
And later I would like to do the following
class User < ActiveRecord::Base
has_many clients
has_many businesses, through: :client
has_many people, through: :client
end

Rails migration join table for multiple associations of the same model using different names

In my project, I have this simple association set up:
class Episode < ActiveRecord::Base
belongs_to :game_master, :class_name => 'CastMember'
has_and_belongs_to_many :players, :class_name => 'CastMember'
end
class CastMember < ActiveRecord::Base
has_and_belongs_to_many :episodes
end
I was wondering what the join table for these might be.
At first, I thought a cast_members_episodes table would be enough, but after thinking about it for a bit, it wouldn't make sense, as it wouldn't be able to differentiate between game_master and players.
Any ideas?
Yes, cast_members_episodes table to handle the many-to-many relation of players and episodes.
And, add a column game_master_id to episodes table to handle the relation of game_master and episodes.

HABTM and accepts_nested_attributes_for

Say I have two models, Book and Author with a has_and_belongs_to_many relationship between them.
What I want to do is to be able to add author names in the book form, and on submit to either link the authors with the book if they already exist, or create them if they don't.
I also want to do the same with the author form: add book names and on submit either link them if they exist, or create them if they don't.
On edit, however, I want to neither be able to edit nor delete the nested objects, only remove the associations.
Is accepts_nested_attributes_for suitable for this, or is there another way?
I managed to accomplish this by following the Complex Forms railscasts on Rails 2, but I'm looking for a more elegant solution for Rails 3.
I'm not sure why so many people use has_and_belongs_to_many, which is a relic from Rails 1, instead of using has_many ..., :through except that it's probably in a lot of old reference books and tutorials. The big difference between the two approaches is the first uses a compound key to identify them, the second a first-class model.
If you redefine your relationship, you can manage on the intermediate model level. For instance, you can add and remove BookAuthor records instead of has_and_belongs_to_many links which are notoriously difficult to tweak on an individual basis.
You can create a simple model:
class BookAuthor < ActiveRecord::Base
belongs_to :book
belongs_to :author
end
Each of your other models is now more easily linked:
class Book < ActiveRecord::Base
has_many :book_authors
has_many :authors, :through => :book_authors
end
class Author < ActiveRecord::Base
has_many :book_authors
has_many :books, :through => :book_authors
end
On your nested form, manage the book_authors relationship directly, adding and removing those as required.

rails, model naming question

I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users

Resources