I have 4 models (Users, Post, Comments, Value) like this:
One user can post something, and someone can comment this post. After that, another user can add a numeric value from 0 to 10 to the comment.
I have done the relation between Users and Post helping me with the rails tutorial, but now i don't know the next step.
I believe you can set up a relation between that value you would like to assign (lets call it rating) and 2 other models.
Based on what you said I guess your relations (associations) would look like this:
class User < ActiveRecord::Base
has_many :post
has_many :comment
has_many :rating, :through => :comment
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comment
has_many :rating, :through => :comment
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
has_many :rating
end
class Rating < ActiveRecord::Base
belongs_to :comment
belongs_to :post
belongs_to :user, :through => :comment
end
Read more about it here
Related
I have a User model and a Book model joined with a Like model.
class Book < ActiveRecord::Base
belongs_to :user
# the like associations
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
class User < ActiveRecord::Base
has_many :books
# the like associations
has_many :likes
has_many :liked_books, :through => :likes, :source => :book
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :book
I want to add an attribute to the Like model so while right now a User can Like a book to add it to their profile, I want the User to be able to write a recommendation.
I generated the attribute Recommendation:text to the Like (joining) model, but am unsure how to add this to views so a User can write a recommendation that will be tied to the Like (and thus that book and user).
I'm looking at this post - Rails has_many :through Find by Extra Attributes in Join Model - which describes something similar but does not explain how to implement this in the views.
Let me know if you can point me in the right direction. Thanks!
I think you should create a separate model for this Recommendation, dependent of the Like model:
class Book < ActiveRecord::Base
belongs_to :user
has_many :comments
has_many :commenters, :through => :comments, :source => :user
class User < ActiveRecord::Base
has_many :books
has_many :comments
has_many :commented_books, :through => :comments, :source => :book
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :book
Then, the logic to create a comment for a book:
# Books Controller
def show
#book = Book.find(params[:id])
#comment = #book.comments.build
end
# show view of the book
form_for #comment do |form|
form.hidden_field :user_id, value: current_user.id
form.hidden_field :book_id
form.text_area :content # the name of the attribute for the content of the comment
form.submit "Post comment!"
end
To list all the comments of a specific User:
# users controller (profile page)
def show
#user = User.find(params[:id])
end
# show view of Users
#user.comments.includes(:book).each do |comment|
"Comment on the book '#{comment.book.name}' :"
comment.content
end
I suppose that the like functionnality works with ajax ? (remote: :true or in pure javascript)
You can append a form with the response of your request with the id of the new like, and again handle the recommendation by an ajax request
Let's consider a practical example: A product that has many reviews, written by clients. We have a many-to-many relationship between product and client through reviews.
class Product < ActiveRecord::Base
has_many :reviews
has_many :clients, :through => :reviews
end
class Client < ActiveRecord::Base
has_many :reviews
has_many :products, :through => :reviews
end
class Reviews < ActiveRecord::Base
belongs_to :product
belongs_to :client
end
Here, I'm using the has_many :through to create the many-to-many relation, because the review table needs to have extra attributes, like the score, content, likes,...
The user logs into my app, so I can get his data through:
client = Client.find_by_id current_user.id
He goes to the product page, so I can get product data:
product = Product.find_by_id params[:id]
How can I create client review of the product?
I tried:
review = Review.create :client => client, :product => product, :comment => params[:review][:comment]
but it gives me: MassAssignSecurity: can't mass-assign protected attributes: product, client
Any idea? Thanks in advance.
As soon as you create a Review object and explicitly pass arguments you need to make them accessible in your Review model. In this case it must be foreign keys
class Reviews < ActiveRecord::Base
belongs_to :product
belongs_to :client
attr_accessible :client_id, :product_id
end
That should work, but this's bad practice, which causes security issues. Instead of making foreign keys accessible and explicitly passing them in Review.create, I recommend to replace review.create with the following:
review = Review.new
review.client = client
review.product = product
review.comment = params[:review][:comment]
review.save
That will create a new Review object avoiding mass-assignment.
Hope this helps.
Add to ur model where the attributes are :product and :client
attr_accessible :product, :client
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
The :client and :product attributes are private, you have to make them accesible on each class setting attr_accessible :client and attr_accessible :product respectively like:
class Reviews < ActiveRecord::Base
belongs_to :product
belongs_to :client
attr_accessible :client, :product
end
Hope this helps
Im setting up a reminder service that sends deals via email in relation to a persons interests AND city.. Basically, the user inputs important dates (friends bday, anniversary ect) and the interests of that special person.
I want to send them deals based on 1)the users city and 2)the interests of the related person
How should i setup my associations for the Deal model?
What i have so far..
class User < ActiveRecord::Base
belongs_to :city
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
class City < ActiveRecord::Base
attr_accessible :name
belongs_to :province
has_many :users
end
class PersonInterest < ActiveRecord::Base
belongs_to :interest
belongs_to :person, :polymorphic => true
end
class Interest < ActiveRecord::Base
has_many :person_interests
end
Thanks!
If a deal could apply to more than one interest, you'd start with something like:
class Deal < ActiveRecord::Base
belongs_to :interests
belongs_to :city
end
class City < ActiveRecord::Base
attr_accessible :name
belongs_to :province
has_many :users
has_many :deals
end
class Interest < ActiveRecord::Base
has_many :person_interests
has_many :deals
end
And then you could do something like
#relevant_deals = #city.deals.where(:interest_id => 'abc')
or
#relevant_deals = #interest.deals.where(:city_id => 'def')
My models look something like this:
class User < ActiveRecord::Base
attr_accessible: :name
has_many :reviews
end
class Product < ActiveRecord::Base
attr_accessible: :name
has_many :reviews
end
class Review < ActiveRecord::Base
attr_accessible: :comment
belongs_to :user
belongs_to :product
validates :user_id, :presence => true
validates :product_id, :presence => true
end
I am trying to figure out what the best way is to create a new Review, given that :user_id and :product_id are not attr_accessible. Normally, I would just create the review through the association ( #user.reviews.create ) to set the :user_id automatically, but in this case I am unsure how to also set the product_id.
My understanding is that if I do #user.reviews.create(params), all non attr_accessible params will be ignored.
You can do:
#user.reviews.create(params[:new_review])
...or similar. You can also use nested attributes:
class User < ActiveRecord::Base
has_many :reviews
accepts_nested_attributes_for :reviews
...
See "Nested Attributes Examples" on http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html.
It seems you would like to implement a many-to-many relationship between a User and Product model, with a Review model serving as a join table to connect the two with an added comment string. This can be accomplished with a has many through association in Rails. Start by reading the Rails Guides on Associations.
When setting up your Review model, add foreign keys for the User and Product:
rails generate model review user_id:integer product_id:integer
And set up your associations as follows:
class User < ActiveRecord::Base
has_many :reviews
has_many :products, through: :reviews
end
class Product < ActiveRecord::Base
has_many :reviews
has_many :users, through: :reviews
end
class Review < ActiveRecord::Base
# has comment string attribute
belongs_to :user
belongs_to :product
end
This will allow you to make calls such as:
user.products << Product.first
user.reviews.first.comment = 'My first comment!'
Here's how you would create a review:
#user = current_user
product = Product.find(params[:id])
#user.reviews.create(product: product)
In the following code a post is created and belongs to Person:
class Person < ActiveRecord::Base
has_many :readings
has_many :posts, :through => :readings
end
person = Person.create(:name => 'john')
post = Post.create(:name => 'a1')
person.posts << post
But I wonder which Reading this post belongs to when it's saved.
I dont quite understand that.
Thanks
post.reading would be nil
Now, I don't think that's what you want, so you'd probably want to protect against that beings saved:
class Reading < ActiveRecord::Base
belongs_to :person
has_many :posts
validates_presence_of :person
end
But, that still seems a bit wrong to me... I'd think that you could have a Person on its own, and a Post on its own, but a reading is the intersection of a Person and a Post. In that case:
class Person
has_many :readings
end
class Post
has_many :readings
end
class Reading
belongs_to :person
belongs_to :post
validates_presence_of :person, :post
end