I am stuck with a design decision. It seems that the answer on my question always depends on the specific situation. Here is my situation:
In my relational database I have a category and sub_category tables already. You can make posts and comments that are either linked to the category or sub_category.
Is it wise to have one posts and one comments table in my database and include a 'type' field in the comment and post tables to distinguish if the post/comment belongs to a category or a sub_category.
Or is it better to split the post and comment tables up into a category_post/category_comment and sub_category_post/sub_category_comment tables?
I am looking for the solution that will optimise speed. I am also looking to follow an architecture pattern that is scalable due to the fact that posts and comments can grow in size quite quickly.
Thanks
The first one, definitely: one posts table and one comments table. I would give each of them a category_id field and a subcategory_id field, so each can link to a category and/or subcategory. I don't think you need the type field.
I'd be tempted to simplify it further and not even have a seperate table for subcategories: instead, make category be a "tree" type model, ie have a category_id field in categories, so categories can be nested. Then you can have a) more than one level of nesting of categories, which is likely to be a requirement at some point and b) have a situation where there's no logical difference between a category and subcategory, other than that defined in the data (ie how they have been nested), which makes your app simpler (and simple = good).
So:
#fields - category_id
Post
has_many :comments
belongs_to :category
#fields - post_id, category_id
Comment
belongs_to :post
belongs_to :category
#fields - parent_id
Category
has_many :posts
has_many :comments
belongs_to :parent, :class_name => "Category", :foreign_key => :parent_id
has_many :children, :class_name => "Category", :foreign_key => :parent_id
Related
At the moment I have categories > posts > comments, and can CRUD the categories, within the categories I can CRUD the Posts (which are specific to categories) and within the Posts I can CRUD the comments, What I now want to be able to do is CRUD Sub-Categories within the Categories, But also CRUD Sub-Categories within Sub-Categories continuously.
So I am really not sure where to start, My thinking is I need to create a model that checks to see if there's a category_id, and if not to check the sub-category_id or something alone these lines?
Any help is appreciated.
Longshanks
First of there is no subcategories by itself. A sub-category is a category with a parent, and that is the clue.
First of you need to add the relation to the model:
belongs_to :parent, :class_name => 'Category', :foreign_key => :parent
has_many :children, :class_name => 'Category', :foreign_key => :parent
Then in your migration:
add_field :categories, :parent_id, :integer
And now you will have the:
childrens = Category.first.children
parent = childrens.first.parent
Available anywhere.
Feel free to rename the children-parent relation as you please but change everything else related.
I have a has_many :through association setup between two tables (Post and Category). The reason I'm using has_many :through instead of HABTM is that I want to do some validation on the join table (PostCategory).
So I have 4 models in use here:
User:
has_many :posts
has_many :categories
Post:
belongs_to :user
has_many :post_categories
has_many :categories, :through => :post_categories
Category:
belongs_to :user
has_many :post_categories
has_many :posts, :through => :post_categories
PostCategory:
belongs_to :post
belongs_to :category
Basically what I want is: Users can create posts, users can also create their own categories. A user can then categorize posts (not just their posts, any posts). A post can be categorized by many different users (in different ways potentially), and a category could contain many different posts (A user could categorize N posts under a specific category of theirs).
Here's where it gets a little bit tricky for me (I'm a Rails noob).
A post can ONLY belong to ONE category for a given user. That is, a post CANNOT belong to more than ONE category for any user.
What I want to be able to do is create a validation for this. I haven't been able to figure out how.
I've tried things like (inside PostCategory)
validates_uniqueness_of :post_id, :scope => :category_id
But I realize this isn't correct. This would just make sure that a post belongs to 1 category, which means that after one user categorizes the post, no other user could.
Really what I'm looking for is how to validate this in my PostCategory model (or anywhere else for that matter). I'm also not against changing my db schema if that would make things easier (I just felt that this schema was pretty straight forward).
Any ideas?
The simpliest way is to add user_id to PostCategory and to validate uniqueness of post_id with user_id scope.
Another way is to create custom validation which checks using sql if category owner has added category to that post.
Option 1 : use a before_save. In it, do a SQL look up to make sure a post with a similar category for your user doesn't exist (take care that on edit, you'll have to make sure you don't look-up for the current Post that is already in the DB)
Option 2 : custom validators :
http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#custom-validators
Never used them, but sounds like it can do what you want
My question is more related to naming conventions than programmation I guess.
Let's assume an application where users can create new articles (so they are the owner of these articles) and where you can add article "editors", who can only update the article content.
class User
include Mongoid::Document
has_many :articles # as owner of the articles
has_and_belongs_to_many :articles # as editor of the articles
end
class Article
include Mongoid::Document
belongs_to :user
has_and_belongs_to_many :editors, :class_name => 'User'
end
What I want to know is how I should call the articles association in my User model. I mean, an article has an author and editors, which seems strong naming conventions to me, but a user has articles he created and articles he is the editor. How would you call/name/declare the last 2 associations?
I would call them as :edited_articles, and :authored_articles or :owned_articles, or something similarly straightforward names. Just dont forget to add the :class_name and :foreign_key or :through qualifiers to them.
Update:
For has_and_belongs_to_many relation you need a connection table, which is by default, is named of the two joined table. E.g. articles_users in your case. In this table you will propably have two ids, user_id and article_id. This way rails connects your models automatically.
has_and_belongs_to_many :editors, :class_name => 'User', :foreign_id => 'user_id'
Of course if you call it editor_id in the join table then use that. And the opposite, on the user side should work too.
How to implement in rails a category that can be child of another category (self-referential has_one) ?
Thanks
First, I believe you want a has_many relationship, not a has_one. I can't imagine a situation where you would want categories to have at most one child category. The previous answer also only gets you one direction - categories know about their parents, but not about their children.
The full solution is simple enough. The categories table should have a category_id column, and the model should look like this:
class Category < ActiveRecord::Base
belongs_to :category
has_many :categories
end
If you want to go a step further and call them parents and children, you can:
class Category < ActiveRecord::Base
belongs_to :parent, :class_name => 'Category', :foreign_key => 'category_id'
has_many :children, :class_name => 'Category', :foreign_key => 'category_id'
end
Good luck with your app!
The simple approach is to use a foreign key of category_id as others have already pointed out. However, if you're talking about nesting lots of categories this can be pretty inefficient. (Note: Posting more of your requirements would be helpful.)
I really, really like the ancestry gem. Here is a relevant snip from the docs (emphasis mine).
As can be seen in the previous
section, Ancestry stores a path from
the root to the parent for every node.
This is a variation on the
materialised path database pattern. It
allows Ancestry to fetch any relation
(siblings, descendants, etc.) in a
single sql query without the
complicated algorithms and
incomprehensibility associated with
left and right values. Additionally,
any inserts, deletes and updates only
affect nodes within the affected
node’s own subtree.
And, here's a bonus freebie, just because it's not immediately obvious. If you need to treat siblings as a list with positions, you can scope them as follows.
acts_as_list :scope => 'ancestry #{(ancestry.blank? ? "IS NULL" : "=\'" + ancestry + "\'")}'
How do I eager-load only some of the objects in a has_many relationship?
Basically, I have four objects: Assignment, Problem, AssignmentUser, and ProblemUser.
#assignment.rb
has_many :problems
has_many :assignment_users
#assignment_user.rb
belongs_to :assignment
belongs_to :user
has_many :problem_users
#problem.rb
belongs_to :assignment
has_many :problem_users
#problem_user.rb
belongs_to :user
belongs_to :problem
belongs_to :assignment_user
attr_accessor :complete #boolean
On a page showing a single assignment, I want to show all of the problems, as well the user's status on each problem, if it exists. (It might not, if this is the first time the user is viewing the page.)
I can't call assignment_user.problem_users and then snake the problems out like so:
-#assignment_user.problem_users.each do |problem_user|
= check_box_tag "problems[#{problem_user.problem.id}]", 1, problem_user.complete
= link_to problem_user.problem.name, assignment_problem_path(assignment_id => problem_user.problem.assignment_id, :id => problem_user.problem_id)
because there might not be ProblemUser entries for every Problem that belongs to an assignment; creating all of those ProblemUser objects whenever someone creates a Problem object would be wasteful, so they're only created on the fly.
What I want is to be able to iterate over the Problems that belong to the particular Assignment, then for each Problem, find a ProblemUser that matches...but without creating an N+1 problem. I could create two arrays, one with all of the problems and one with all of the problem_users, but then I would have to match them up, right? Maybe that's the best way... but any recommendations on best practices are appreciated here.
Try using :include something along the lines of...
#assignment.rb
has_many :problems, :include => :problem_user
has_many :assignment_users
Assuming a field named description in each of the tables assignments, problems, and problem_users the solution should resemble this...
Assignment.find(1).problems.collect { |a| [a.assignment.description, a.description, a.problem_user.description] }