What is the relationship between these two tables in RoR? - ruby-on-rails

I am developing an application like the stackoverflow, which questions or articles have at less one tag. And one tags must have one or more articles.
So, I am doing this in migration in RoR. I am consider which relationship is suitable for both table. In article table, should use a "has_many", and in the tag table, should use "has_many".
But I am thinking is it necessary to add one more table in the middle, something like....
So, the first one is like that:
class Article < ActiveRecord::Base
has_many :tags
end
class Tag < ActiveRecord::Base
has_many :articles
end
or something like this:
class Article < ActiveRecord::Base
has_many :articleTagList
has_many :tags, :through => : articleTagLists
end
class Tag < ActiveRecord::Base
has_many :articleTagList
has_many :articles, :through => :articleTagLists
end
class ArticleTagList < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end

Many-to-Many relationships in a normalized database will always need a third "look-up table."
If you denormalize you can get away with just having the tag id's in one field with a delimiter between them. But you also have to provide the logic to handle retrieval.
I'd personally just go with the normalized option.

If you don't want to store any information on the middle table (for example the name of the user who added tag X to the question Y), you can use the has_and_belongs_to_many:
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
If you want to store something, you need to create the middle model, as your example. In your example, the ArticleTagList model should be called ArticlesTag and the database table should be articles_tags, by convention.

Related

Modeling a Subscription in Rails

So, after thinking on this for a while, I have no idea what the proper way to model this is.
I have a website focused on sharing images. In order to make the users happy, I want them to be able to subscribe to many different collections of images.
So far, there's two types of collections. One is a "creator" relationship, which defines people who worked on a specific image. That looks like this:
class Image < ActiveRecord::Base
has_many :creations
has_and_belongs_to_many :locations
has_many :creators, through: :creations
end
class Creator < ActiveRecord::Base
has_many :images, ->{uniq}, through: :creations
has_many :creations
belongs_to :user
end
class Creation < ActiveRecord::Base
belongs_to :image
belongs_to :creator
end
Users may also tag an image with a subjective tag, which is something not objectively present in the image. Typical subjective tags would include "funny" or "sad," that kind of stuff. That's implemented like this:
class SubjectiveTag < ActiveRecord::Base
# Has a "name" field. The reason why the names of tags are a first-class DB model
# is so we can display how many times a given image has been tagged with a specific tag
end
class SubjectiveCollection < ActiveRecord::Base
# Basically, "User X tagged image Y with tag Z"
belongs_to :subjective_tag
belongs_to :user
has_many :images, through: :subjective_collection_member
end
class SubjectiveCollectionMember < ActiveRecord::Base
belongs_to :subjective_collection
belongs_to :image
end
I want users to be able to subscribe to both Creators and SubjectiveTags, and to display all images in those collections, sequentially, on the home page when they log in.
What is the best way to do this? Should I have a bunch of different subscription types - for example, one called SubjectiveTagSubscription and one called CreatorSubscription? If I do go this route, what is the most efficient way to retrieve all images in each collection?
What you want to use is a Polymorphic Association.
In your case, it would look like this:
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :subscribeable, polymorphic: true
end
The subscriptions table would need to include the following fields:
user_id (integer)
subscribeable_id (integer)
subscribeable_type (string)
This setup will allow a subscription to refer to an instance of any other model, as ActiveRecord will use the subscribeable_type field to record the class name of the thing being subscribed to.
To produce a list of images for the currently logged in user, you could do this:
Subscription.where(user_id: current_user.id).map do |subscription|
subscription.subscribeable.images.all
end.flatten
If the performance implications of the above approach are intolerable (one query per subscription), you could collapse your two types of subscribeables into a single table via STI (which doesn't seem like a good idea here, as the two tables aren't very similar) or you could go back to your initial suggestion of having two different types of subscription models/tables, querying each one separately for subscriptions.

How should I design/structure my database and ActiveRecord associations? has_many :through?

Using Rails 4 (and Postgres), I'm trying to work out the best way in which to structure my ActiveRecord associations and corresponding database tables/relationships for a diet tracking app.
I want my app to be structured as follows:
I have Document and FoodEntry models. Each Document has a number of FoodEntries. I want to be able to iterate over them like document.food_entries.each ... (which is easy with your typical has_many association).
However all the FoodEntries for each Document need to be able (potentially but not necessarily) to be subdivided by day, as this is a natural division for which logic and calculations must be able to be performed, in addition to doing them for the whole document. For instance I'd be using something like document.day(1).food_entries.each ....
Furthermore, each day should be able to be subdivided (again, optionally) into meals in a similar manner, e.g. document.day(1).meal(1).food_entries.each ...
Lastly, there must be a way to record the user-specified order that the FoodEntries, meals, and days are in for each document. Presumably using number sequence(s)?
I was thinking there are a few ways I could do this:
Use a simple has_many relationship. Have day, meal and sort columns in the food_entries table, where the value for day and meal is left blank or given a default value if a day/meal isn't provided. Use a logic-based approach to get and sort the entries for a day or meal.
Outline:
class Document
has_many :food_entries
class FoodEntry
belongs_to :document
Potential issues:
This might leave things a bit messy in general in the table?
All the logic for subdividing things would have to be hand-coded.
Storing/using the user-defined (i.e. arbitrary) sort order might get a bit complicated? The order for entries AND days AND meals would have to stored in and inferred from one sequence (unless more columns were added).
Use has_many :through to set up associations through days and meals (naming?) tables. Entries where a day/meal isn't specified get given a default. Both these tables have their own individual sort column, along with the food_entries table.
Outline:
class Document
has_many :days
has_many :meals, through: :days
has_many :food_entries, through: :days (AND :meals???)
class Day
belongs_to :document
has_many :meals
has_many :food_entries, through: :meals
class Meal
belongs_to :day
has_many :food_entries
class FoodEntry
belongs_to :meal
Potential issues:
Adds unnecessary relational complexity? (consider that days or at the very least meals are meant to be optional)
Can I even use has_many :food_entries through: ... in my Document model if it would have to go through both tables?
A compromise between the two approaches above: have a days table but keep meal in a column in the food_entries table.
Something else? Polymorphic association(s)?
This is getting a bit complicated to wrap my head around, and so I'm really having a hard time working out what I should use. What is the correct way to go about things?
A couple of final questions which are related but completely optional:
Ideally the day value could be either a datetime value or an arbitrary string, depending on what the user sets. Is this possible?
Could anyone point me to a resource that can inform me about sorting/ordering strategies? Like I said I assume the simplest way is to use a sequence of numbers, but I'm not exactly sure how I would work with such a sequence.
has_many :through
You'd only use a has_many :through relationship if you wanted to attribute multiple FoodEntries to Document, like this:
#app/models/document.rb
Class Document < ActiveRecord::Base
has_many :food_entries_types
has_many :food_entries, through: :food_entries_types
end
#app/models/food_entry_type.rb
Class FoodEntryType < ActiveRecord::Base
belongs_to :document
belongs_to :food_entry
end
#app/models/food_entry.rb
Class FoodEntry < ActiveRecord::Base
has_many :food_entries_types
has_many :documents, through: :food_entries_types
end
This would only allow you to associate many food_entries with a similar number of documents. Although you could add specific days & meals attributes to the join model, allowing you to call them as required
Scopes
I believe a much better option for you is to use ActiveRecord scopes:
#app/models/document.rb
Class Document < ActiveRecord::Base
has_many :food_entries
#uses [wday][3]
#needs to return dates for specific day
scope :day, ->(day = 1) { where(created_at: Date::DAYNAMES[day]) }
scope :meal, ->(meal = 1) { where(meal: meal) }
end
Because scopes can be chained, I believe you'd be able to do this:
food = Document.day(1).meal(2).food_entries
Class Method
You could also create a class_method to achieve something similar:
#app/models/document.rb
Class Document < ActiveRecord::Base
has_many :food_entries
def self.sorted(day = 1, meal = 1)
document = self.where("created_at = ? AND meal = ?", Date::DAYNAMES[day], meal)
end
end
#app/controllers/documents_controller.rb
def show
#document = Document.sorted
end
I ended up implementing my #2 option. This has given me the most flexibility and worked out well. I think it has been the most elegant approach for my use case.

3 or more model association confusion at Rails

It has been almost a week since I'm trying to find a solution to my confusion... Here it is:
I have a Program model.
I have a ProgramCategory model.
I have a ProgramSubcategory model.
Let's make it more clear:
ProgramCategory ======> Shows, Movies,
ProgramSubcategory ===> Featured Shows, Action Movies
Program ==============> Lost, Dexter, Game of Thrones etc...
I want to able to associate each of these models with eachother. I've got what I want to do particularly with many-to-many association. I have a categories_navigation JOIN model/table and all of my other tables are connected to it. By this way, I can access all fields of all of these models.
BUT...
As you know, has_many :through style associations are always plural. There is nothing such as has_one :through or belongs_to through. BUT I want to play with SINGULAR objects, NOT arrays. A Program has ONLY ONE Subcategory and ONLY ONE Category. I'm just using a join table to only make connection between those 3. For example, at the moment I can access program.program_categories[0].title but I want to access it such like program.program_category for example.
How can I have 'has_many :through's abilities but has_one's singular usage convention all together? :|
P.S: My previous question was about this situation too, but I decided to start from scratch and learn about philosophy of associations. If you want so you may check my previous post here: How to access associated model through another model in Rails?
Why a join table where you have a direct relationship? In the end, a program belongs to a subcategory, which in turn belongs to one category. So no join table needed.
class Program < ActiveRecord::Base
belongs_to :subcategory # references the "subcategory_id" in the table
# belongs_to :category, :through => :subcategory
delegate :category, :to => :subcategory
end
class Subcategory < ActiveRecord::Base
has_many :programs
belongs_to :category # references the "category_id" in the table
end
class Category < ActiveRecord::Base
has_many :subcategories
has_many :programs, :through => :subcategories
end
Another point of view is to make categories a tree, so you don't need an additional model for "level-2" categories, you can add as many levels you want. If you use a tree implementation like "closure_tree" you can also get all subcategories (at any level), all supercategories, etc
In that case you skip the Subcategory model, as it is just a category with depth=2
class Program < ActiveRecord::Base
belongs_to :category # references the "category_id" in the table
scope :in_categories, lambda do |cats|
where(:category_id => cats) # accepts one or an array of either integers or Categories
end
end
class Category < ActiveRecord::Base
acts_as_tree
has_many :programs
end
Just an example on how to use a tree to filter by category. Suppose you have a select box, and you select a category from it. You want to retrieve all the object which correspond to any subcategory thereof, not only the category.
class ProgramsController < ApplicationController
def index
#programs = Program.scoped
if params[:category].present?
category = Category.find(params[:category])
#programs = #programs.in_categories(category.descendant_ids + [category.id])
end
end
end
Tree-win!

Rails - data view from has_many and Has_many_and_belongs_to_many

I'm trying to implement this project:
http://img7.imagebanana.com/img/cnb46ti2/relationships.png
I want to let view the skills of an employee on the employee's show page
An employee has a position, and every position has skills which an employee of this position needs to know
so if I understand right, positions and skills have an n:m relationship, and they need a join table for a has_many_and_belongs_to_many relationship. Because a position includes many skills and every skill belongs to many positions.
now my questions
the position_skill-table -> is it better to use a has_and_belongs_to_many relationship, so this table has no own id or is it better to use a has_many :through relationship? I guess it's better do use a has_and_belongs_to_many relationship, because this relationship table will not have any further information inside than just the two keys. Am I right?
if I take a has_and_belongs_to_many - relationship, is that the only thing I need to write into the models?
a) class Position < ActiveRecord :: Base (...) has_and_belongs_to_many :skills (...)
b) class Skill < ActiveRecord :: Base (...) has_and_belongs_to_many :positions (...)
c) into db\migrate def self.up create_table :positon_skill, :id => false do |t| (...)
and after that, the positions and skills are connected with each other? Is that right? Did I forget something?
if that's right, how can I let the skills view on employee's show page? An employee has 1 position, and this position has several skills... What for code do I need to write into the show.html.erb of employee? Something like <%= employee.position.skill %>? Do I also need to render something? Sorry, I'm very confused and I think I read too much information in web... Or is there any description in web which exactly describes what I need for?
thanks alot in advance and sorry for that redundant question.
If you're sure you aren't going to want to add any information later to the position_skills table, has_and_belongs_to_many will work fine. However, has_many :through is far more flexible if you change your mind later and isn't much harder to set up.
If you use has_and_belongs_to_many, you only need association declarations in the models and the database table with position_id:integer and skill_id:integer fields. Seems like you've got that already.
To be able to access employee.position.skills in your view, you need to eagerly load the employee's associations. Try something like the following:
class EmployeesController < ApplicationController
...
def show
#employee = Employee.find(params[:id], :include => { :position => :skills })
end
...
end
I think that should work if you stick with has_and_belongs_to_many, but if you go for has_many :through (which I recommend), you'll need to use :include => { :position => { :position_skills => :skills } }
This is what it looks like in your diagram. Consider the following:
class Employee < ActiveRecord :: Base
belongs_to :position
...
end
class Position < ActiveRecord :: Base
has_many :employees
has_many :position_skills
has_many :skills, :through => :position_skills
...
end
class PositionSkill < ActiveRecord :: Base
belongs_to :position
belongs_to :skill
...
end
class Skill < ActiveRecord :: Base
has_many :position_skills
has_many :positions, :through => :position_skills
...
end
The only problem with this is that an employee is tied to a single position. While this position has many skills through positions. I would change it to position belongs_to employee and employee has_many positions. This leaves it open to track employees that move from one position to the next. Let me know if you need further info on that.

How do I do multiple has_and_belongs_to_many associations between the same two classes?

I have the following setup:
class Publication < ActiveRecord::Base
has_and_belongs_to_many :authors, :class_name=>'Person', :join_table => 'authors_publications'
has_and_belongs_to_many :editors, :class_name=>'Person', :join_table => 'editors_publications'
end
class Person < ActiveRecord::Base
has_and_belongs_to_many :publications
end
With this setup I can do stuff like Publication.first.authors. But if I want to list all publications in which a person is involved Person.first.publications, an error about a missing join table people_publications it thrown. How could I fix that?
Should I maybe switch to separate models for authors and editors? It would however introduce some redundancy to the database, since a person can be an author of one publication and an editor of another.
The other end of your associations should probably be called something like authored_publications and edited_publications with an extra read-only publications accessor that returns the union of the two.
Otherwise, you'll run in to sticky situations if you try to do stuff like
person.publications << Publication.new
because you'll never know whether the person was an author or an editor. Not that this couldn't be solved differently by slightly changing your object model.
There's also hacks you can do in ActiveRecord to change the SQL queries or change the behavior of the association, but maybe just keep it simple?
I believe you should have another association on person model
class Person < ActiveRecord::Base
# I'm assuming you're using this names for your foreign keys
has_and_belongs_to_many :author_publications, :foreign_key => :author_id
has_and_belongs_to_many :editor_publications, :foreign_key => :editor_id
end

Resources