class User < ActiveRecord::Base
has_many :books
has_many :book_users
has_many :books, :through => :book_users
end
class Book < ActiveRecord::Base
belongs_to :user
has_many :book_users
has_many :users, :through => :book_users
end
An user can write many books
An book can belong to only one user
An user can be a reader of different books
An book can be read by different users
User.books
should give me the books the user has written
User.books_read
should give me the books, are read by this user
How accomplish this ?
Second question, what is the simplest method to delete book_read from the user ?
I mean
User.method_name(book_id) # what's the method name ?
First question:
You either use :source or :class_name.
has_many :books_read, :class_name => "Book", :through => :book_users
I don't know for sure if :class_name works with has_many :through. If it doesn't work, try this:
has_many :books_read, :source => :book, :through => :book_users
That should do the trick.
Second question:
As far as I know there isn't really a simple method to delete books from the books_read relation. You could create your own method to accomplish this. Just make sure you delete records from :book_users and not the has_many :through relation. Or else the books themselves will be deleted.
def delete_book(book_id)
self.book_users.find_by_book_id(book_id).destroy
end
When using Rails 3 you can't use the find_by_... helper and need to use where.
def delete_book(book_id)
self.book_users.where(:book_id => book_id).destroy
end
Now you can call this function as follows:
User.find(1).delete_book(2)
I hope that helps you out.
Related
I'm attempting to create a system where my site's users can favorites pages. Those pages have two types, either clubs or sports. So, I have four models, associated as such:
User Model:
class User < ActiveRecord::Base
..
has_many :favorites
has_many :sports, :through => :favorites
has_many :clubs, :through => :favorites
..
end
Favorites Model:
class Favorite < ActiveRecord::Base
..
belongs_to :user
belongs_to :favoritable, :polymorphic => true
end
Club Model:
class Club < ActiveRecord::Base
..
has_many :favorites, :as => :favoritable
has_many :users, :through => :favorites
def to_param
slug
end
end
Sport Model:
class Sport < ActiveRecord::Base
..
def to_param
slug
end
..
has_many :favorites, :as => :favoritable
has_many :users, :through => :favorites
..
end
Essentially, the User has_many sports or clubs through favorites, and the association between favorites, sports, and clubs is polymorphic.
In practice, this is all working exactly the way I want it to, and the whole system I have designed works. However, I'm using Rails_Admin on my site, and I get an error in three places:
When loading the Dashboard (/admin) the first time. If I refresh the page, it works fine.
When loading the User model in Rails_Admin
When loading the Favorites model in Rails_Admin
Here is the error message on /admin/user (gist). All of the errors are similar, referencing ActiveRecord::Reflection::ThroughReflection#foreign_key delegated to source_reflection.foreign_key, but source_reflection is nil:.
Can anyone point me in the right direction so that I can fix this? I've searched all over, and asked other programmers/professionals, but no one could spot the error in my models. Thanks so much!
Alright, well, I finally worked this out, and figured that I'd post the fix just in case it helps someone else out in the future (no one likes finding someone else with the same problem and no posted answer).
As it turns out, with a polymorphic has_many :through, there is a little more configuration needed. My User model should have looked like this:
class User < ActiveRecord::Base
..
has_many :favorites
has_many :sports, :through => :favorites, :source => :favoritable, :source_type => "Sport"
has_many :clubs, :through => :favorites, :source => :favoritable, :source_type => "Club"
..
end
This answer to another question about polymorphic has_many :through associations is what helped me figure this out.
I encountered this error when the code included a has_many for an association that doesn't exist (mid-refactor). So it can also be caused by some general has_many misconfigure. The Ruby/Rails code never cares because the dynamic style of Ruby means the association is only called on demand. But Rails-Admin exhaustively inspects properties, leading to reflection problems.
Total newbie to Rails and programming in general, so please forgive me if the answer to my question seems glaringly obvious. I've been doing a lot of reading including the ROR guides but can't seem to find a scenario specific to the following situation:
I have three models scaffolded under one User model, and am trying to link them like so:
class User < ActiveRecord::Base
has_many :malls, :dependent => :destroy
end
class Mall < ActiveRecord::Base
belongs_to :user
has_many :stores, :dependent => :destroy
has_many :cakes, :as => :cake_poly, :dependent => :destroy
end
class Store < ActiveRecord::Base
belongs_to :mall
has_many :cakes, :as => :cake_poly, :dependent => :destroy
end
class Cakes < ActiveRecord::Base
belongs_to :cake_poly, :polymorphic => true
end
Will this work? If it does, is there a better way to implement this? If not, how else can I implement the associations?
The idea is that each User may have many malls, each Mall may have many stores, and both malls and stores may have many cakes.
A key question for your design is the intent - what do you want to achieve?
You should not use the polymorphic relationship if you simply like to aggregate cakes available in the mall's shops for the mall model. You can use the following relationship to achieve such a design:
class Mall < ActiveRecord::Base
has_many :stores
has_many :cakes, :through => :stores
end
The polymorphic approach is a perfect choice if the two sets (cakes in the mall, cakes in the mall's stores) don't correlate.
What is the correct way to describe the relationship between a User and the Outcomes of their Questions and Contacts? I want to be able to call User.outcomes and get all outcomes for the user, regardless of whether the outcome was for a question or a contact.
Here are my models as they stand right now. Are the has_many through relationships described correctly?
User Model
has_many :questions
has_many :contacts
has_many :outcomes, through: :questions
has_many :outcomes, through: :contacts
Question Model
has_many :outcomes
Contact Model
has_many :outcomes
Outcomes Model
belongs_to :question
belongs_to :contact
So, this is probably not the ideal solution because it returns an Array instead of an ActiveRecord::Relation. That means you lose lazy loading and the ability to further add scopes and where statements and whatnot. It's better than writing SQL and should do what you want though:
class User < ActiveRecord::Base
has_many :questions
has_many :contacts
has_many :questions_outcomes, :through => :questions, :class_name => "Outcomes"
has_many :contacts_outcomes, :through => :contacts, :class_name => "Outcomes"
def outcomes
return questions_outcomes + contacts_outcomes
end
end
Please let us know if you come up with something nicer.
Can't wrap my head around this...
class User < ActiveRecord::Base
has_many :fantasies, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasy < ActiveRecord::Base
has_many :users, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasizing < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... which works fine for my primary relationship, in that a User can have many Fantasies, and that a Fantasy can belong to many Users.
However, I need to add another relationship for liking (as in, a User "likes" a Fantasy rather than "has" it... think of Facebook and how you can "like" a wall-post, even though it doesn't "belong" to you... in fact, the Facebook example is almost exactly what I'm aiming for).
I gathered that I should make another association, but I'm kinda confused as to how I might use it, or if this is even the right approach. I started by adding the following:
class Fantasy < ActiveRecord::Base
...
has_many :users, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class User < ActiveRecord::Base
...
has_many :fantasies, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class Approval < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... but how do I create the association through Approval rather than through Fantasizing?
If someone could set me straight on this, I'd be much obliged!
Keep your first set of code, then in your User Model add:
has_many :approved_fantasies, :through => :fantasizings, :source => :fantasy, :conditions => "fantasizings.is_approved = 1"
In your Fantasizing table, add an is_approved boolean field.
Say there're two models : User and Post, i want to keep track of who has read which post, and who writes which post, which user favorites which posts etc. Then i come up with the following solution:
class User < ActiveRecord::Base
has_many :writings
has_many :posts, :through => :writings
has_many :readings
has_many :posts, :through => :readings
#..
end
class Post < ActiveRecord::Base
has_many :writings
has_many :posts, :through => :writings
#...
end
and setting up the intermediate models - Writing, Reading. this works, but finally i find out that when i writing this
#user.posts #...
the returned array contains housekeeping information both for writings and readings. How can i solve this problem.
You want something like this:
class User < ActiveRecord::Base
has_many :writings
has_many :posts, :through => :writings
has_many :readings
has_many :read_posts, :through => :readings, :class_name => "Post"
#..
end
By giving the association name something other than just :posts you can refer to each one individually. Now...
#user.posts # => posts that a user has written via writings
#user.read_posts # => posts that a user has read via readings