Let's say we use ActiveRecord and there's a user (User model) having many comments (Comment model) and many articles (Article model). We can write this:
class User < ActiveRecord::Base
has_many :comments
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
end
...so I can do user.comments and user.articles:
user.comments # => [#<Comment:0x12bfcd010>, #<Comment:0x3928c1101>]
user.articles # => [#<Article:0x10aacd333>]
Considering Article and Comment as an item, is that possible to do so (with 1 query)?
user.items # => [#<Comment:0x12bfcd010>, #<Article:0x10aacd333>, #<Comment:0x3928c1101>]
...This way, an item could looks like a polymorphcal attached resource. Even if it's not a normal polymorphic association.
I think this would be possible. Thanks.
This has been asked before, best to google for reverse polymorphic associations. That said, here's the question asking the same thing.
Reverse Polymorphic Associations
And the answer then links to this gist, which illustrates a way to do it pretty well.
https://gist.github.com/1242485
Related
I have the following two models, User..
class User < ActiveRecord::Base
has_and_belongs_to_many :sites
end
.. and Site:
class Site< ActiveRecord::Base
has_and_belongs_to_many :users
end
Up to this point its fine. It works and it's pretty simple.
Now I want to introduce "primary user" to the Site. I add "primary_user_id" to the Site, and trying to add a second association:
class Site< ActiveRecord::Base
has_and_belongs_to_many :user
# my new association that doesn't work...
has_one :primary_user, :class_name => "User", :conditions => ['id = ?', '{self.primary_user_id}']
end
It doesn't like it... Now I know that I can fake this by just adding a method "primary_user" to the site and this will work, but my question is whether it is possible to user ActiveRecord associations and how?
has_and_belongs_to_many is tricky and most people have moved away from it and use has_many through => model.
btw - 'Up to this point its fine. It works and it's pretty simple.' is how all things start off. How they perform when you 'really' start to use them is what counts and for that reason you'll probably find has_many through easier to work with.
These links will help:
http://paulbarry.com/articles/2007/10/24/has_many-through-checkboxes
http://thoughtsincomputation.com/posts/checkboxes-with-has_many-through
http://my.opera.com/durrantm/blog/2011/07/24/rails-simple-form-with-has-many-through-hmt-relationship
https://github.com/romanvbabenko/nested_has_many_through (nesting gem).
I have a weird design question. I have a model called Article, which has a bunch of attributes. I also have an article search which does something like this:
Article.project_active.pending.search(params)
where search builds a query based on certain params. I'd like to be able to limit results based on a user, that is, to have some articles have only a subset of users which can see them.
For instance, I have an article A that I assign to writers 1,2,3,4. I want them to be able to see A, but if User 5 searches, I don't want that user to see. Also, I'd like to be able to assign some articles to ALL users.
Not sure if that was clear, but I'm looking for the best way to do this. Should I just store a serialized array with a list of user_id's and have -1 in there if it's available to All?
Thanks!
I would create a join table between Users and Articles called view_permissions to indicate that a user has permission to view a specific article.
class ViewPermission
belongs_to :article
belongs_to :user
end
class User
has_many :view_permissions
end
class Article
has_many :view_permissions
end
For example, if you wanted User 1 to be able to view Article 3 you would do the following:
ViewPermission.create(:user_id => 1, :article_id => 3)
You could then scope your articles based on the view permissions and a user:
class Article
scope :viewable_by, lambda{ |user| joins(:view_permissions).where('view_permissions.user_id = ?', user.id) }
end
To search for articles viewable by a specific user, say with id 1, you could do this:
Article.viewable_by(User.find(1)).project_active.pending.search(params)
Finally, if you want to assign an article to all users, you should add an viewable_by_all boolean attribute to articles table that when set to true allows an article to be viewable by all users. Then modify your scope to take that into account:
class Article
scope :viewable_by, lambda{ |user|
joins('LEFT JOIN view_permissions on view_permissions.article_id = articles.id')
.where('articles.viewable_by_all = true OR view_permissions.user_id = ?', user.id)
.group('articles.id')
}
end
If an Article can be assigned to multiple Writers and a Writer can be assigned to multiple Articles, I would create an Assignment model:
class Assignment < AR::Base
belongs_to :writer
belongs_to :article
end
Then you can use has_many :through:
class Article < AR::Base
has_many :assignments
has_many :writers, :through => :assignments
end
class Writer < AR::Base
has_many :assignments
has_many :articles, :through => :assignments
end
I am new to rails and read this guide to get all the info so far.
I have a simple scenario and want to make sure whether or not my associations will work fine.
Scenario:
User logs in -> sets up many groups -> each group has many employees
User model:
class User < ActiveRecord::Base
has_many :groups
end
Group model:
class Group < ActiveRecord::Base
belongs_to :user
has_many :employees
end
Employee model:
class Employee < ActiveRecord::Base
has_many :groups
belongs_to :group
end
Questions
Will this work for the scenario I mentioned?
I am confused about how to get all Employees under a User. What would be the code for that?
If I need typical CRUD for all these models then would would be in my Action? index/create/update/destroy? Can someone point me to a good guide on actions?
I also like the has_many through --
class User < ActiveRecord::Base
has_many :groups
has_many :employees, :through=>:groups
end
Then you can go:
user = User.find(23)
user.employees.do_something
Otherwise, you could loop through your groups and its employees (kinda ugly, but will work)
User.first.groups.each{|group| group.employees.each{|employee| puts employee.name}}
You have it together, for the most part, but I think you need to look at has_and_belongs_to_many (which you will frequently see abbreviated as habtm.) Index, create, update, and destroy would be your CRUD list for Ruby on Rails. As for a good guide, I like Agile Web Development With Rails, by Dave Thomas. (When I'm picking up a new topic, I like books - electronic or otherwise.) It's available online through The Practical Programmers. The question about "what's a good guide" is pretty subjective, so caveat emptor.
How do I destroy the association itself and leave the objects being associated alone, while keeping this RESTful?
Specifically, I have these models:
class Event < ActiveRecord::Base
has_many :model_surveys, :as => :surveyable, :dependent => :destroy, :include => :survey
has_many :surveys, :through => :model_surveys
end
class ModelSurvey < ActiveRecord::Base
belongs_to :survey
belongs_to :surveyable, :polymorphic => true
end
class Survey < ActiveRecord::Base
has_many :model_surveys
end
That's saying that the Event is :surveyable (ModelSurvey belongs_to Event). My question is, without having to create a ModelSurveysController, how do I destroy the ModelSurvey, while leaving the Event and Survey alone?
Something with map.resources :events, :has_many => :model_surveys? I'm not quite sure what to do in this situation. What needs to happen with the routes, and what needs to happen in the controller? I'm hoping the url could look something like this:
/events/:title/model_surveys/:id
Thanks for your help,
Lance
In Rails 2.3 you have accepts_nested_attributes_for which would let you pass an array of ModelSurveys to the event in question. If you allow destroy through the nested attributes declaration, you'll be able to pass event[model_surveys][1][_destroy]=1 and the association will be removed. Check out the api docs.
Resources domain != model domain
The domain of the controller is not the same as that of the models. It's perfectly fine to update multiple models by changing the state of a resource.
In your case that means doing a PUT or POST to either the Event or the Survey which contains a list of ids for the other. The model for one will update the association.
PUT or POST
Some people (but not Roy Fielding) believe that you should use a PUT to update the resource and provide all of the state again, others feel that a POST with the partial state (ala PATCH) is sufficient.
I have two models, Article and Post that both inherit from a base model called ContentBase.
You can leave comments on both Articles and Posts, so I am using a Polymorphic Association between Comments and Article or Post.
However, since both Article and Post inherit from ContentBase, the commentable_type field ends up being "ContentBase" for both and screws everything up.
Is there a way to specify the commentable_type field in the has_many relationship in Article and Post?
Edit:
By "screws everything up" I mean if there is an Article with ID=1 and Post with ID=1 and I add a Comment with commentable_id=1, commentable_type=ContentBase, that comment will show up for both the Article and Post.
Here's the code:
class Article < BaseContent
has_many :comments, :as => :commentable
end
class Post < BaseContent
has_many :comments, :as => :commentable
end
and here's my Comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
What's in the ContentBase class? Can you move that code into a module instead of using inheritance?
Module BaseContent
def self.included(base)
base.class_eval do
validates_presence_of :somefield
validates_length_of :someotherfield
def my_method
"hello"
end
end
end
end
I don't think you want to do that. For polymorphic associations, you want the XXX_type field to be the base model class, not the actual class. I'm not exactly sure of the reason, but I believe it has to do with determining the table name to select from to get the polymorphic data.
I think you need to look at Single Table Inheritance, which is what ActiveRecord uses for storing derived classes in the database. It assumes that since Article and Post are subclasses of ContentBase, they will all be in the same table ("content_bases" by default). If that's the case, you'll never have an Article with ID=1 and a Post with ID=1.
A few references:
Rails Single Table Inheritance | Juixe Technow
Single Table Inheritance in Ruby on Rails