I'm trying to create a review system on users in rails. I want one user to be able to rate another user on their profile page in devise. I've tried a few different methods but I am fairly new to rails and haven't been able to accomplish this.
Right now I have default devise views but no user profile page. I'd like users to review a another user on 5 or so different issues.
Any help would be much appreciated!
In order to do that, you can use the association called has_many through association :
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Your models should look like that "
class User < ActiveRecord::Base
has_many :rates
has_many :rated_users, through: :rates, class_name: "User", foreign_key: :rated_user_id # The users this user has rated
has_many :rated_by_users, through: :rates, class_name: "User", foreign_key: :rating_user_id # The users that have rated this client
end
class Rates < ActiveRecord::Base
belongs_to :rating_user, class_name: "User"
belongs_to :rated_user, class_name: "User"
end
And your migrations :
class createRates < ActiveRecord::Migration
def change
create_table :changes do |t|
t.belongs_to :rated_user
t.belongs_to :rating_user
t.integer :value
t.timestamps
end
end
end
Oxynum - great concept! After adding models and applying migrations, starts with templates. Starting point for you is a users_controller.rb. Probably, you already have a 'show' action inside UsersController. This action available for authenticated users.
Modify this action to smth like:
class UsersController < ApplicationController
before_filter :authenticate_user!
before_filter :load_ratable, :only => [:show, :update_rating]
def show
# Renders app/views/users/show.html.erb with user profile and rate controls
end
def update_rating
my_rate_value = params[:value] == 'up' ? +1 : -1
if #rated_by_me.blank?
Rate.create(rated_user: #userProfile, rating_user: #user, value: my_rate_value)
flash[:notice] = "You rated #{#userProfile.name}: #{params[:value]}"
else
flash[:notice] = "You already rated #{#userProfile.name}"
end
render action: 'show'
end
protected:
def load_ratable
#userProfile = User.find(params[:id]) # - is a viewed profile.
#user = current_user # - is you
#rated_by_me = Rate.where(rated_user: #userProfile, rating_user: #user)
end
end
Add to routes:
get 'users/update_rating/:value' => 'user#update_rating'
Start rails server, Log In, and try to change rating directly:
http://localhost:3000/users/update_rating/up
Related
I'm setting up my rails association for an app and I'm not sure if my associations are correct for my use case. The use case is: A product can be added once by a user. Once created other users can then add the same product to their own "feed" within the app. I want to be able to do User.products to list all of a users products. And for products I want to be able to do something like Product.where(id: 2).users to list all of the users that have added the product. I'm currently using a has_and_belongs_to_many association but I think that this is incorrect for what I am trying to achieve?
User model: has_and_belongs_to_many :products
Product model: has_and_belongs_to_many :users
add_index "products_users", ["product_id"], name: "index_products_users_on_product_id"
add_index "products_users", ["user_id"], name: "index_products_users_on_user_id"
Do this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :created_products, class_name: "Product", foreign_key: :user_id #-> created product
has_and_belongs_to_many :products #-> list of products
end
#app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :user #-> created the product
has_and_belongs_to_many :users #-> list of users
end
You'll need to add the appropriate foreign_key to your User model (user_id in the Product model for the belongs_to :user association) --
--
If your has_and_belongs_to_many relationship is working already, the above should be sufficient.
If not, you need to look up this documentation to see how it works, and then create a join table called products_users (which is populated with the appropriate data):
$ rails g migration CreateProductsUsers
#db/migrate/create_products_users______.rb
class CreateProductsUsers < ActiveRecord::Migration
def change
create_table :products_users, id: false do |t|
t.references :product
t.references :user
end
end
end
$ rake db:migrate
It will allow you to create a single product for a user (IE the Product object will have a direct association with the user who created it). The Product and User models will also be joined with the habtm relationship.
In your controllers, you could use the following:
#config/routes.rb
resources :products #-> url.com/products
scope "profile" do
resources :products, only: :index #-> url.com/profile/products
end
This will allow you to use the following:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
before_action :product, only: :edit
def index
#products = current_user.products #-> if you're using Devise
end
def edit
#product = current_user.created_products.find params[:id]
end
def new
#product = current_user.created_products.new
end
def create
#product = current_user.created_products.new product_params
#product.save
end
private
def product
redirect_to root_path, notice: "This is not your product" unless current_user.products.exists? params[:id]
end
def product_params
params.require(:product).permit(:x, :y, :z)
end
end
To be able using has_and_belongs_to_many create association, you must create one temperator table container 2 column are product_id, user_id
you can refer
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
In Ruby on Rails 4, how do you create a many-to-many relationship inside a relationship model for a friends list such as Facebook using the has_many :through ... syntax ?? I'm a newbie and currently learning Ruby on Rails 4. I have looked at this link.
But still have a hard time grasping it.
you will need a join table that references both sides of the relations
let us say you have an relation Post and another relation Category with a many to many relationship between them you need a join table to be able to represent the relationship.
migration for a join table would be
class CreateCategoriesPosts < ActiveRecord::Migration
def change
create_table :categories_posts do |t|
t.integer :category_id
t.integer :post_id
t.timestamps
end
add_index :categories_posts, [:category_id, :post_id]
end
end
and in the models/post.rb
Class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end
and in the models/category.rb
Class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
more here:
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
I think #RAF pretty much nailed it. But to use the OP's example:
class User < ActiveRecord::Base
has_and_belongs_to_many :users_list
end
class UsersList < ActiveRecord::Base
has_and_belongs_to_many :users
end
Although at first it might seem like a User should have only one list of friends (UsersList), that might not always be the case. Think of types within the UserList model, such as: 'close friends', 'work friends', 'all friends' for example.
My advice: dig into the Rails guides. This is a concept worth learning and truly understanding (which I'm still doing :).
many-to_many relationships are a simple concept, but complex when using the database because of the way databases work. A person could have 1 to N different friends, which means that a single entry for a database would need a dynamic amount of memory for each entry, which in the db world is a no-no. So instead of creating a list of friends you would have to make a table that represents the links between friends, for example:
friendship.rb
class Friendship < ActiveRecord::Base
belongs_to :friend, foreign_key: 'friend_A' # this entry has a field called 'friend_A'
belongs_to :friend, foreign_key: 'friend_B' # this entry has a field called 'friend_B'
end
These links will represent your network of friends. However, as the two previous answers have mentioned, Rails has some nifty magic, "has_and_belongs_to_many", which will do this for you.
NOTICE: The problem here is that in my StatusesController, in the index action, the #relationship object only gets the statuses of all your friends, but does not get your own statuses. Is there a better way of approaching this? I am trying to create a view to view all statuses of users that are your friends, and your own statuses too, and so far, I can't seem to figure out how to order it chronologically, even if in my status model, i included "default_scope -> { order(created_at: :desc) } ". Any advice would be deeply appreciated
class User < ActiveRecord::Base
has_many :relationships
has_many :friends, :through => :relationships
has_many :inverse_relationships, class_name: 'Relationship', foreign_key: 'friend_id'
has_many :inverse_friends, through: 'inverse_relationships', :source => :user end
#
class Relationship < ActiveRecord::Base
# before_save...
belongs_to :user
belongs_to :friend, class_name: 'User'
end
#
class RelationshipsController < ApplicationController
def friend_request
user_id = current_user.id
friend_id = params[:id]
if Relationship.where( user_id: user_id, friend_id: friend_id, accepted: false).blank?
Relationship.create(user_id: user_id, friend_id: friend_id, accepted: false)
redirect_to user_path(params[:id])
else
redirect_to user_path(params[:id])
end
end
def friend_request_accept
# accepting a friend request is done by the recipient of the friend request.
# thus the current user is identified by to_id.
relationship = Relationship.where(user_id: params[:id], friend_id: current_user.id).first
if Relationship.exists?(relationship) and relationship.accepted == false
relationship.update_attributes(accepted: true)
end
redirect_to relationships_path
end
def friend_request_reject
relationship = Relationship.where(user_id: params[:id], friend_id: current_user.id).first
relationship.destroy
redirect_to relationships_path
end
################################
def index
#relationships_pending = Relationship.where(friend_id: current_user.id, accepted: false)
end
end
#
class StatusesController < ApplicationController
def index
#status = Status.new
#relationship = Relationship.where('friend_id = ? OR user_id = ?', current_user.id, current_user.id).
where( accepted: true)
end
def new
#status = Status.new
end
end
#
I want to limit the amount of records a user can add to the database.
I'm not sure of the 'Rails' way to go about this...
I am using Devise and thought of creating a custom validation method but you can't access current_user from within a model and it isn't correct.
How can I do this from the controller and still return an error message to my users?
I had this
validate :post_count
def post_count
current = current_user.posts.count
limit = current_user.roles.first.posts.count
if current > limit
errors.add(:post, "Post limit reached!")
end
end
but it isn't the correct way to go about it as it would be hacky to get the current_user into the model
You could do something like this:
class User < ActiveRecord::Base
has_many :domains
has_many :posts, through: :domains
def post_limit
roles.first.posts.count
end
end
class Domain < ActiveRecord::Base
belongs_to :user
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :domain
delegate :user, to: :domain
validate :posts_count_within_limit, on: :create
def posts_count_within_limit
if self.user.posts(:reload).count >= self.user.post_limit # self is optional
errors.add(:base, 'Exceeded posts limit')
end
end
end
Based on this answer.
In Ruby on Rails I have a user models and a jobs model joined through a different model called applicants. I have a button for the users when they want to "remove their application for this job" but I don't know how to remove the specific user, and for that matter I don't know if I'm doing a good job at adding them either (I know atleast it works).
user.rb
class User < ActiveRecord::Base
...
has_many :applicants
has_many:jobs, through: :applicants
end
job.rb
class Job < ActiveRecord::Base
...
has_many :applicants
has_many:users, through: :applicants
end
applicant.rb
class Applicant < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
when someone applies for a job my jobs controller is called:
class JobsController < ApplicationController
...
def addapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id)
redirect_to #job
end
...
end
Does that .update indicate that whatever is there will be replaced? I'm not sure if I'm doing that right.
When someone wants to remove their application I want it to go to my jobs controller again but I'm not sure what def to make, maybe something like this?
def removeapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id).destroy
redirect_to #job
end
does it ave to sort through the list of user_ids save them all to an array but the one I want to remove, delete the table then put them all back in? I'm unsure how this has_many works, let alone has_many :through sorry for the ignorance!
thanks!
Let's assume the user will want to remove their own application. You can do something like this:
class UsersController < ApplicationController
def show
#applicants = current_user.applicants # or #user.find(params[:id]), whatever you prefer
end
end
class ApplicantsController < ApplicationController
def destroy
current_user.applications.find(params[:id]).destroy
redirect_to :back # or whereever
end
end
And in your view:
- #applicants.each do |applicant|
= form_for applicant, method: :delete do |f|
= f.submit
Don't forget to set a route:
resources :applicants, only: :destroy
Some observations, I would probably name the association application instead of applicant. So has_many :applications, class_name: 'Applicant'.
I have followed this tut http://railsapps.github.com/tutorial-rails-bootstrap-devise-cancan.html I want to do something like this:
before_filter :authenticate_user!
before_filter :authenticate_VIP!
before_filter :authenticate_admin!
before_filter :authenticate_somerole!
I have tables: roles, users, user_roles and I don't want to create another table (rails g devise VIP create another table).
I want to have methods authenticate_ROLE. How to do this ?
I have three table, Users, Roles, and RoleRelationships (or role_users, it's up to you)
This is my Role table:
class Role < ActiveRecord::Base
attr_accessible :name
has_many :role_relationships
has_many :users, through: :role_relationships
end
Role table will have name column for roles, like: "admin", "teacher", "vip" (as you want).
And this is User table:
class User < ActiveRecord::Base
devise ...
has_many :role_relationships
has_many :roles, through: :role_relationships
end
and my RoleRelationship table:
class RoleRelationship < ActiveRecord::Base
attr_protected :role_id, :user_id
belongs_to :user
belongs_to :role
end
I set up my app one user can have many roles, you can set up your way. So, i have a role?(role) method in my user.rb, like this:
def role?(role)
return role == RoleRelationship.find_by_user_id(self.id).role.name
end
Then in my abilities files, i define abilities of users:
def initialize(user)
user ||= User.new # guest user
if user.role? "teacher"
can :read, Course
can :manage, Topic, user_id: user.id
can :create, Topic
else user.role? "admin"
can :manage, Course
end
So, teacher will only read Course, and admin can CRUD Course. To do that, i use method load_and_authorize_resource in my CoursesController:
class CoursesController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
...
end
Finally, in my views, i used code like this:
<% if can? manage, #course %>
Only admin can work, see what happen here.
<% end %>
So, as you see, teacher only can read Course so they can't see or do what admin can do, in this case, is create course or edit course.
This is what i built in my online test app, you can reference and do the same for your app.