I'm trying to do user reviews, when user can write review to another user, i create tables review with :content, user_reviews with :for_user_id, and by_user_id,
my routes
devise_for :users
resources :users, :only => [:show] do
resources :reviews
end
class Review < ActiveRecord::Base
belongs_to :user
has_many :users, :through => :users_reviews
end
class User < ActiveRecord::Base
has_many :users_review
has_many :reviews, :through => :users_review
end
class UsersReview < ActiveRecord::Base
belongs_to :user
belongs_to :review
end
class ReviewsController < ApplicationController
def new
#user = User.find(params[:user_id])
#review = #user.reviews.new(params[:for_user_id])
end
def create
#user = User.find(params[:id])
#review = current_user.reviews.build(review_params)
redirect_to root_path
end
def show
end
def index
#user = User.find(params[:for_user_id])
#reviews = Review.all
end
private
def review_params
params.require(:review).permit(:user_id, :user_id, :content)
end
end
and my view
<%= form_for([#user, #user.reviews.build]) do |f| %>
<%= f.text_area :content, placeholder: "Your review" %>
<%= f.submit "Go", class: "btn btn-large btn-primary" %>
<% end %>
all work, but no data send to the db :\ what i doing wrong?
You're not saving anything in the create method, therefore nothing is going to persist.
You'll want something like:
def create
#user = User.find(params[:id])
#review = #user.reviews.build(review_params)
if #user.save && #review.save
redirect_to root_path
else
(handle bad data)
end
end
I would also tend to agree with #marzapower - If you want to use current_user, you don't need the line above #review. My method above has this change included.
You are missing a call to the save method in your create action:
def create
#user = User.find(params[:id])
#review = current_user.reviews.build(review_params)
#review.save
redirect_to root_path
end
This post explains the difference between build and create.
I think you're missing two things. The User and Review classes both need to reference the UserReview class that they employ in the through relationship, e.g.,
class User < ActiveRecord::Base
...
has_many :user_reviews_received, class_name: 'UserReview', foreign_key: :for_user_id
has_many :reviews_received, through: :user_reviews_received, class_name: 'Review'
has_many :user_reviews_written, class_name: 'UserReview', foreign_key: :by_user_id
has_many :reviews_written, through: :user_reviews_written, class_name: 'Review'
...
end
As noted above, the create action seems a bit confused. Generally speaking the params should be the same for building up an instance in the new and create actions but you've got some differences. You could resolve this in one of two ways. One option would be to resolve it in the controller.
class ReviewsController < ActionController::Base
def new
#user = User.find(params[:user_id])
#review = #user.reviews_received.new(by_user_id: current_user.id)
end
def create
#review = #user.reviews_received.create(params[:review].merge(by_user_id: current_user.id))
redirect_to root_path
end
end
The second option would be to resolve it in the view (new first line in the form).
<%= form_for([#user, #user.reviews_received.build(by_user_id: current_user.id)]) do |f| %>
<%= f.hidden_field :by_user_id %>
<%= f.text_area :content, placeholder: "Your review" %>
<%= f.submit "Go", class: "btn btn-large btn-primary" %>
<% end %>
I would tend towards something like the first option
There's an error in the controller, I think:
def create
#user = User.find(params[:id])
#review = #user.reviews.build(review_params)
#review.save
redirect_to root_path
end
You were creating new Reviews for current_user instead of #user.
Related
I am new to rails and I am creating a app in which a clone like twitter. The users are connected with each other by sending request first and the corresponding user accepts or deletes the request. I followed michael-hartl book.
User model:
attr_accessible :email, :name, :password, :password_confirmation, :id
has_many :reverse_requests, foreign_key: "requested_id", class_name: "Request", dependent: :destroy
has_many :requesters, through: :reverse_requests, source: :requester
has_many :requests, foreign_key: "requester_id", dependent: :destroy
has_many :requested_users, through: :requests, source: :requested
Accept or decline view:
<ul class="users">
<% #users.each do |user| %>
<%= link_to gravatar_for(user, size: 30), user %>
<%= link_to user.name, user %>
//Accept which invokes create
<%= form_for(current_user.relationships.build(follower_id: user.id)) do |f| %>
<%= f.hidden_field :follower_id %>
<%= f.submit "Accept", class: "btn btn-large btn-primary" %>
<% end %>
//Decline which invokes destroy
<%= form_for(user.requests.find_by_requested_id(current_user),
html: { method: :delete }) do |f| %>
<%= f.submit "Decline", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
</ul>
Requests controller:
def create
#user = User.find(params[:request][:requested_id])
current_user.request!(#user, 2)
#current_user.status!(2)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Request.find(params[:id]).requester
current_user.decline!(#user)
respond_to do |format|
format.html { redirect_to current_user }
format.js
end
end
When decline is clicked the request is removed from the database as well as from the user requests list. Which I need it also after accepting the request it should be removed from both the database and request list.
Can anyone help to achieve this?
Is it possible to call the destroy function after the accept button is clicked?
Or any other suggestions??
Answering to your question, YES we can call the destroy action. But I suggest we go with something more scale and pretty design, here is my rough design (not tested):
# routes.rb
Rails.application.routes.draw do
resources :requests, only: [] do
member do
patch :accept
patch :decline
end
end
end
# request.rb
class Request < ApplicationRecord
belongs_to :user
def accept
# 1. Do accept logic
# 2. Destroy
destroy
end
def decline
# 1. Do decline logic
# 2. Destroy
destroy
end
end
# requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request
def accept
#request.accept
# Redirect somewhere
redirect_to profile_path
end
def decline
#request.decline
# Redirect somewhere
redirect_to profile_path
end
private
def set_request
#request = Request.find(params[:id])
end
end
With the following design you and add more logic easily to decline/accept e.g send email ...
I am new to Ruby on Rails.I am facing a problem using nested resources.
I am building a learning app where there are courses and lessons.
Every course will have many lessons and a lesson belongs to only one course.
I am unable to create a lesson for a course currently.
Example : http://localhost:3000/courses/19/lessons/new is a page where i want to create and display lessons for course 19.
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :courses
resources :courses do
resources :lessons
end
resources :lessons
root 'pages#landing'
get 'pages/home' => 'pages#home' ,as: :home
get '/user/:id' => 'pages#profile',as: :profile
get '/users' => 'courses#index',as: :user_root
end
Course.rb
class Course < ActiveRecord::Base
belongs_to :user
has_many :lesson
validates :user_id , presence: true
end
Lesson.rb
class Lesson < ActiveRecord::Base
belongs_to :course
validates :course_id , presence: true
end
CourseController.rb
class CoursesController < ApplicationController
def index
#courses = Course.all;
end
def new
#course = Course.new;
end
def create
#course = Course.new(course_params);
#course.user_id = current_user.id;
if #course.save
redirect_to course_path(#course)
else
flash[:notice]="Course could not be created ! "
redirect_to new_course_path
end
end
def edit
end
def update
end
def destroy
#course = Course.find(params[:id]);
#course.destroy;
end
def show
#course = Course.find(params[:id]);
end
private
def course_params
params.require(:course).permit(:title, :description, :user_id)
end
end
LessonController.rb
class LessonsController < ApplicationController
def index
#lessons = Lesson.all;
end
def new
#lesson = Lesson.new;
end
def create
#lesson = Lesson.new(lesson_params);
#course = Course.find_by(id: [params[:course_id]]);
if #lesson.save
redirect_to new_course_lesson_path , flash[:notice] = "Lesson successfully saved !"
else
redirect_to new_course_lesson_path , flash[:notice] = "Lesson cannot be created ! "
end
end
def show
#lesson = Lesson.find(params[:id])
end
private
def lesson_params
params.require(:lesson).permit(:title,:description,:video,:course_id)
end
end
Lessonform.html.erb
<%= form_for ([#course,#lesson]) do |f| %>
<%= f.label :lesson_Title %>
<%= f.text_field :title ,placeholder: "Enter the lesson Title" ,:class=>"form-control" %><br />
<%= f.label :Description %>
<%= f.text_area :description ,placeholder: "Enter the lesson Description",rows:"8",:class=>"form-control" %><br />
<center>
<%= f.submit "Create lesson",:class =>"btn btn-lg btn-primary" %>
</center>
<% end %>
One problem i see is that you have defined route resources :lessons twice. Once, inside courses scope and second time outside.
The error seems to occur because in your view #course is nil. So, please check you set #course in a before_action inside lessons_controller#new action.
EDIT
class LessonsController < ApplicationController
before_action :set_course, only: [:new, :create]
def new
#lesson = #course.lessons.build
end
private
def set_course
#course = Course.find_by(id: params[:course_id])
end
end
Also replace has_many :lesson with has_many :lessons inside Course model.
First change you need to make in your Course model as you have singular lesson when defining many association:
has_many :lessons
Also let me know if their are any chances of lessons page being called without courses? If no then please remove:
resources :lessons
I guess also the two defining of courses in routes in creating issue. Please try removing the:
resources :courses
Let me know if you still face any issue.
I have three models: User, Publisher and Interest all with many to many relationships linked through three join models but only 2 out of 3 join models record the id's of their 2 parent models. my UsersPublisher model does not link User to Publisher.
My Interestscontroller proccesses a form (see code) through which I ask the user to provide Interest and Publisher. The latter gets processed via the fields_for method which allows you to pass Publisher attributes via the InterestsController. the UsersPublisher join model records the user_id but the publisher_id is nil.
I've tried putting #users_publishers in both the new and create methods of Publishers- and InterestsController. My latest attempt of using after_action in the InterestsController (see code) has also failed. I've also tried the after_action way in the PublishersController
Your helped is highly appreciated!
The UsersPublisher join model
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
end
InterestsController
class InterestsController < ApplicationController
before_action :find_user
after_action :upublisher, only: [:new]
def index
#interests = policy_scope(Interest)
end
def show
#interest = Interest.find(params[:id])
end
def new
#interest = Interest.new
#interest.publishers.build
authorize #interest
end
def create
#interest = Interest.new(interest_params)
#users_interests = UsersInterest.create(user: current_user, interest: #interest)
authorize #interest
if #interest.save
respond_to do |format|
format.js
format.html {redirect_to root_path}
end
flash[:notice] = 'Thank you, we will be in touch soon'
else
respond_to do |format|
format.js { render }
format.html { render :new }
end
end
end
def edit
#interest = Interest.find(params[:id])
authorize #interest
end
def update
#interest = Interest.find(params[:id])
#interest.update(interest_params)
if #interest.save
flash[:notice] = 'Your interest has been added'
else
flash[:notice] = 'Oops something went wrong'
end
end
private
def interest_params
params.require(:interest).permit(:name, publishers_attributes: [:publisher,:id, :feed])
end
def find_user
#user = current_user
end
def upublisher
#users_publishers = UsersPublisher.create(publisher: #publisher, user: current_user)
end
end
Form
<%= form_for [#user, #interest] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :publishers do |ff| %>
<%= ff.label :publisher %>
<%= ff.text_field :publisher %>
<%= ff.label :feed %>
<%= ff.text_field :feed %>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Since you're using fields_for, you'll want to make sure you have accepts_nested_attributes_for:
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
This should fix your issue (if it's as you outlined).
Your question is pretty broad, so I don't know whether the above will work. Below are my notes...
From the looks of it, your structure is very complicated; you should work to make it as simple as possible. In the case of creating "interests", you may wish to get rid of the form completely:
#config/routes.rb
resources :publishers do
resources :interests, path: "interest", only: [:create, :destroy] #-> url.com/publishers/:publisher_id/interest
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
before_action :set_publisher
def create
current_user.interests.create publisher: #publisher
end
def destroy
#interest = current_user.interests.find_by publisher_id: #publisher.id
current_user.interests.delete #interest
end
private
def set_publisher
#publisher = UserPublisher.find params[:publisher_id]
end
end
You'd be able to use the above as follows:
<%= link_to "Add Interest", publisher_interest_path(#publisher), method: :post %>
<%= link_to "Remove Interest", publisher_interest_path(#publisher), method: :delete %>
Thinking about it properly, you've got a pretty bad structure.
I'd do something like this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :interests
has_many :publishers, through: :interests
end
#app/models/interest.rb
class Interest < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
#app/models/publisher.rb
class Publisher < ActiveRecord::Base
has_many :interests,
has_many :users, through: :interests
end
This should give you the ability to create interests for any number of users and publishers. If you create a publisher for a specific user, you can use accepts_nested_attributes_for to pass the appropriate data:
#config/routes.rb
resources :users do
resources :interest, only: [:new, :create, :destroy] #-> url.com/users/:user_id/interests/new
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
def new
#user = User.find params[:user_id]
#interest = #user.interests.new
#interest.publisher.build
end
def create
#user = User.find params[:user_id]
#interest = #user.interests.new interest_params
end
private
def interest_params
params.require(:interest).permit(:user, :publisher)
end
end
#app/views/interests/new.html.erb
<%= form_for [#user, #interest] do |f| %>
<%= f.fields_for :publisher do |p| %>
<%= p.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
I am trying to set up a 5 star rating system so users can rate other users. At the moment everything is working, (create, delete, update etc...) but only the logged in user can rate himself. I cannot rate other users. I get no errors, it just redirects to the user profile page as it should but without added a rating to that user.
user.rb
class User < ActiveRecord::Base
has_many :reviews
review.rb
class Review < ActiveRecord::Base
belongs_to :user
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :find_user
before_action :find_review, only: [:edit, :update, :destroy]
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to user_path(#user)
else
render 'new'
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to user_path(#user)
else
render 'edit'
end
end
def destroy
#review.destroy
redirect_to user_path(#user)
end
private
def review_params
params.require(:review).permit(:rating, :comment)
end
def find_user
#user = User.find(params[:user_id])
end
def find_review
#review = Review.find(params[:id])
end
end
_form which then gets rendered on show page:
<%= simple_form_for([#user, #user.reviews.build]) do |f| %>
<div id="rating-form">
<label>Rating</label>
</div>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
<script>
$('#rating-form').raty({
path: '/assets/',
scoreName: 'review[rating]'
});
</script>
Any help getting this to work would be greatly appreciated!!
Do this:
#config/routes.rb
resources :users do
resources :reviews, only: [:new, :create]
end
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :reviewed, class_name: "User", foreign_key: :reviewed_id
end
#app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
def new
#review = current_user.reviews.new
end
def create
#review = current_user.reviews.new review_params
#review.save
end
private
def review_params
params.require(:review).permit(:rating, :comment).merge(reviewed_id: params[:user_id])
end
end
#app/views/reviews/new.html.erb
<%= form_for #review do |f| %>
<%= f.number_field :rating %>
<%= f.text_field :comment %>
<%= f.submit %>
<% end %>
This would mean you'll have to include a reviewed_id column in your reviews table.
You'll be able to access it using: url.com/users/:user_id/reviews/new
The application will automatically fill the user_id and reviewed_id fields, so the rest of your code should work with the upgrade.
The big problem you have is that you're basically recording the user_id (presumably of who created the review)... but have no way of stipulating who the review is about.
The above code fixes that for you.
I tried to implement a commenting system on my Ruby on Rails website.
I basically followed these guidelines from this thread: Micropost's comments on users page (Ruby on Rails)
However, the comment doesn't show when I post and the text "Asset" is displayed on top of every comment box. Where is this coming from?
Updated with codes:
I am using three models to try to get the comments working as shown in the above link
user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :comments
micropost.rb
class Micropost < ActiveRecord::Base
attr_accessible :content, :image, :comment_content
belongs_to :user
has_many :comments, dependent: :destroy
validates :user_id, presence: true
validates :content, presence: true
default_scope order: 'microposts.created_at DESC'
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
comment.rb
class Comment < ActiveRecord::Base
attr_accessible :comment_content
belongs_to :user
belongs_to :micropost
validates :comment_content, presence: true
validates :user_id, presence: true
validates :micropost_id, presence: true
end
comments controller
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = Comment.new(params[:comment])
#comment.micropost = #micropost
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render 'shared/_comment_form'
end
end
end
micropost controller
class MicropostsController < ApplicationController
before_filter :signed_in_user
before_filter :correct_user, only: :destroy
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_path
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_path if #micropost.nil?
end
end
user controller
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
#comment = Comment.new
end
comment form
<%= form_for([micropost, #comment]) do |f| %>
routes.rb
resources :microposts do
resources :comments
end
micropost view
<li>
<span class="content"><%= simple_format(micropost.content) %></span>
<%= image_tag micropost.image_url(:thumb).to_s %><br>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<%= render 'shared/comment_form', micropost: micropost %>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %><br>
<% end %>
</li>
Maybe you have some static informations in one of your partials or in the form control that render your textarea for the comment?
Check you view for the controller or maybe it is somewhere in the 'static_pages/home'
EDIT:
Just full search your project for the text asset, maybe you find an image alt="assets" tag for an image and the image is not available.