Rails - creating and editing in a different controller/model - ruby-on-rails

I have a nested reviews model with a polymorphic association with majors, careers, and schools. I want users to review these majors, schools, and careers on their show page. In addition, I want them to see all the reviews they've made on their profile page.
I can get the reviews to show but I am having trouble with adding, editing, and deleting them because I want them to be able to do these actions when they see their review on the major, school, career, or their profile page.
Here are my routes:
resources :majors, :schools, :careers do
resources :reviews
end
Here are my associations:
Major.rb, School.rb, Career.rb
has_many :reviews, as :reviewable
Review.rb
belongs_to :user
belongs_to :reviewable, polymorphic: true
User.rb
has_many :reviews
Here is my reviews_controller.rb:
before_filter :reviewable
def index
#reviews = #reviewable.reviews
#major = Major.includes(:reviews => :user).find_by_slug(params[:id])
end
def show
#review = #reviewable.reviews.find(params[:id])
end
def new
#review = #reviewable.reviews.build
#review.user = current_user
end
def edit
#review = #reviewable.reviews.find(params[:id])
raise(ActiveRecord::RecordNotFound, "Access Denied") if #review.user != current_user
end
def create
#review = #reviewable.reviews.build(params[:review])
#review.user = current_user
#reviews = #reviewable.reviews
end
def destroy
#review = #reviewable.reviews.find(params[:id])
#review.destroy
raise(ActiveRecord::RecordNotFound, "Access Denied") if #review.user != current_user
end
def reviewable
#reviewable = if params[:major_id]
Major.find_by_slug!(params[:major_id])
elsif params[:school_id]
School.find(params[:school_id])
elsif params[:career_id]
Career.find(params[:career_id])
end
end
In order to create, view, and edit a review for a major, for instance, they will be doing it on the major show page. This is what I have on majors_controller.rb:
def show
#reviews = Review.includes(:user)
#reviews_most_liked = Review.order("created_at DESC").limit(4)
end
I'm trying to make it so they can add and edit from a modal window inside the major, school, and career show page. Here's what I have in the modal window:
<%= simple_form_for([#reviewable, #review]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :review %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>

I figured it out. The problem was with mapping the right instance variables with the correct action and controller. Most of instance variables were placed in my majors_controller.rb, school_controller.rb, and careers_controller.rb show action. This is what part of my majors_controller.rb show action - I was mistakenly placing them in the create action and delete action. But, once I sat and thought about it I realized it's all happening within the show page. Here's what my controller for my models look like:
def show
#reviewable = #major
#reviews = #reviewable.reviews #show reviews
#reviews_most_liked = #reviewable.reviews.order("created_at DESC").limit(2)
#reviews_most_liked_2 = #reviewable.reviews.order("created_at DESC").limit(2).offset(2)
#review = #reviewable.reviews.build(params[:review]) #create a review
#review.user = current_user #connect the created review to the user
end
Then in my majors show.html.erb page I was able to call the show method by calling the person who gave the review:
<%= review.user.profile_name %>
and the review itself:
<%= review.review %>
That displays the review. I then needed to create, edit and delete. In order to create I just had to get the instance variables correct. Once I got them correct in the right controller and under the correct action (majors controller show action) then in the modal window on the majors show page I called the form partial:
<%= render 'reviews/form' %>
which looks like this:
<%= simple_form_for([#reviewable, #review]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :review, :input_html => { :class => "span4", :rows => 10 },
label: false,
placeholder: 'Help others by sharing what you\'ve learned as a major.' %>
</div>
<div class="modal-footer">
<button class="btn cancel-modal-review" data-dismiss="modal" aria-hidden="true">Cancel</button>
<%= f.submit 'Share My Review', :class => "submit-feedback" %>
</div>
<% end %>

Related

Ruby on rails - create Invitation on Project page without render new action

I try to create a project page with a form to send invitation to other users. The Owner (who have created the project) can invite other users to participate to the project.
Right now, Here is the code :
views/projects/show.html.erb
<div class="container">
<h3> <%= #project.title %> </h3>
<h6> Créé par <%= link_to #project.owner.username, user_path(#project.owner) %> </h6>
<hr>
<h3> Inviter des utilisateurs au projet </h3>
<!-- form for search users -->
<%= form_tag new_invite_path, method: :post, :class => 'form-inline' do %>
<div class="form-group">
<%= text_field_tag :search, params[:search], size: 30, class: 'form-control' %>
</div>
<%= submit_tag 'Ajouter au projet', class: 'btn btn-success' %>
<% end %>
<!-- end form for search users -->
<!-- display users results -->
<% #users.each do |user| %>
<p> <%= user.username %> | <%= user.email %> </p>
<% end %>
<!-- end display results -->
</div>
controllers/projects_controller.rb
class ProjectsController < ApplicationController
def show
#project = Project.find(params[:id])
#users = User.search(params[:search])
end
def new
#project = Project.new
end
def create
#project = Project.new(project_params)
#project.owner = current_user
#project.members = []
if #project.save
puts #project
redirect_to user_path(current_user)
else
puts 'something went wrong'
puts #project.errors.full_messages
render 'new'
end
end
private
def project_params
params.require(:project).permit(:title, :description, :client, :deadline, :owner, :members)
end
end
On the project page, I have an Ajax form to find all the users, with their username and email.
Now, when I submit this form, I want to create an invitation (a notification, but I haven't begin the notification system). So, I have created this model :
class Invite
include Mongoid::Document
field :email
belongs_to :project
belongs_to :sender_id, :class_name => 'User'
belongs_to :recipient_id, :class_name => 'User'
end
And a controller :
class InvitesController < ApplicationController
def new
#invite = Invite.new(email: params[:search], sender_id: current_user.id)
byebug
#invite.save
end
def create
#invite = Invite.new(params[:search])
if #invite.save
flash[:success] = 'the invitation is send'
redirect_to user_path(current_user)
else
render 'projects/show'
end
end
end
So as you can see, I want to save the invite in my db (MongoDB -> Mongoid), but when I submit the form (on the project/show page), I have this error :
No route matches [POST] "/invites/new"
It's normal, but I want to know :
how to insert data in my database without rendering a view ?
how to have access to user ID with the email adresse ? (which is in the DB)
Thank you !
NB: don't hesitate to ask if you need more code to answer
1) You can insert the data on the database without rendering anything with this line on the controller render :nothing => true, :status => 200
so your create method will be like this
def create
#invite = Invite.new(params[:search])
if #invite.save
flash[:success] = 'the invitation is send'
render :nothing => true, :status => 200
else
render 'projects/show'
end
end
and this is wrong No route matches [POST] "/invites/new" when you try to create something, you will need to go to create, not the new action, just change the url on the form, because you are pointing to the wrong action.
2) If you have an User model and want to load an user by email, you can do something like this
User.find_by_email("the email of the user")
this is your model is User and the column where the email is, is named "email"

Form resulting in blank post with no ID

I am new to Rails and working on creating a generic "facebook" type of app as practice with users and posts associated with each user. However, I'm currently having an issue where I think the form that I am using to create the posts is also being rendered out as a blank post with no post ID where I display all of the posts in a section below. I think that this post is being shown even before it is being saved to the database.
Here is my code in my view:
<div class="newpostcontainer">
<div class="newposttext">
<%= form_for([#user, #user.posts.build]) do |f| %>
<%= f.text_area :post, size: "69x1" %>
</div>
<div class="newpostsubmitbutton">
<%= f.submit %>
</div>
<% end %>
</div>
<% #user.posts.reverse_each do |p| %>
<div class="postedcontainer">
<div class="minipostpic">
<%= image_tag #user.photo.url, width: 32, height: 32 %>
</div>
<div class="nameofposter"><%= #user.name %></div>
<div class="dateofpost"><%= p.created_at%></div>
<div class="postcontent"><%= p.id%></div> <br>
<div class="postcontent"><%= p.post%></div> <br>
<div class="likecommentdelete">
<%= link_to "Delete", [p.user, p], method: :delete %> | Like | Comment
</div>
</div>
<%end%>
</div>
Here is my controller:
def index
#user = User.find(params[:user_id])
#posts = #user.posts.all
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.create!(post_params)
redirect_to user_path(#user)
end
def show
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
#post.destroy
if #post.destroy
redirect_to user_path(#user)
else
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit!
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates_presence_of :post
end
I'm pretty sure the issue has something to do with my form to create the new post because when I remove it or comment it out, the extra blank post with no post ID goes away.
Any thoughts or suggestions?
Thank you!!
I think you need to permit the field values to be posted:
i.e.,
params.require(:post).permit!
should be
params.require(:post).permit(:name, :post)
then only it will POST I think.
Hope it helps :)
This is because of rails 4 strong parameter feature. You need to whitelist your active models parameters. For more details refer to here.
In your case you need to do something like this:
params.require(:post).permit(:post)
where the ":post" inside require is your model and the other one is your permitted field that is your textarea.
Several issues -
Form
<%= form_for([#user, #user.posts.build]) do |f| %>
Why are you building an associative object? #user.posts.build will not persist your data, and will cause all sorts of non-conventional issues I would highly recommending building the posts associative object in your controller's new action before using in the view, so you can do this:
#app/controllers/users_controller.rb
def new
#user = current_user
#user.posts.build
end
<%= form_for #user do |f| %>
Association
You're trying to edit the post attribute with this statement:
<%= f.text_area :post, size: "69x1" %>
This won't work in any circumstance, as :post is an association, not an object. Rails only allows you to change / add attributes to specific objects, which means you'll be better doing something like this:
<%= f.fields_for :posts do |p| %>
<%= p.text_area :title %>
<%= p.text_area :body %>
<% end %>
Strong Params
You're currently permitting all your params? You'll be better doing this:
def post_params
params.require(:user).permit(posts_attributes: [:title, :body])
end
Use Posts Controller
A better way will be to just use the posts_controller, like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<% end %>

Nested form fields_for text_area is not displaying

I have three-tier model:
User has_many Asks has_many Outcomes
On the home page, I would like the user to be able to add an Outcome to their Ask when they mark it complete. I'm trying to use a nested form to display the Outcome description in the Ask form which also updates the done flag and done date.
Like other users/questions here on SO, I cannot get a nested form to display on the screen. I've followed instructions from the other questions, but still the nested field is not displaying. Am wondering if someone can spot the issue in the code below?
Ask Model
class Ask < ActiveRecord::Base
attr_accessible :category, :description, :done, :followed_up,
:helper, :public, :date_done, :date_followed_up, :user_id, :outcomes_attributes
belongs_to :user, counter_cache: true
has_many :outcomes
accepts_nested_attributes_for :outcomes
end
Ask Controller
class AsksController < ApplicationController
def new
#ask = current_user.asks.build(params[:ask])
#ask.outcomes.build
end
def create
#ask = current_user.asks.build(params[:ask])
if #ask.save!
respond_to do |format|
format.html { redirect_to edit_ask_path(#ask) }
format.js
end
else
flash[:error] = "Something is wrong. The Ask was not saved..."
end
end
def edit
#ask = current_user.asks.find(params[:id])
end
def update
#ask = current_user.asks.find(params[:id])
#ask.outcomes.build
#ask.update_attributes(params[:ask])
respond_to do |format|
format.html { redirect_to edit_ask_path(#ask) }
format.js
end
end
end
Home Page Controller (this form is on the home page)
class StaticPagesController < ApplicationController
def home
if signed_in?
#ask = current_user.asks.build(params[:ask])
#ask.outcomes.build
end
end
Form Partial rendered on the home page
<% if current_user.asks.any? %>
<ul id="ask-list-items">
<% current_user.asks.where(done: false).each do |a| %>
<%= form_for(a) do |f| %>
<li><%= a.description %></li>
<%= f.hidden_field :date_done, value: Date.today %>
<%= f.hidden_field :done, :value=>true %>
<%= f.submit "Mark as done", class: "btn btn-small hidden done_btn", id: "a-#{a.id}-done" %>
<%= f.fields_for :outcomes do |builder| %> # << These fields are not showing up
<%= builder.text_area :description, placeholder: "Describe the outcome...", id: "ask-message" %>
<% end %>
<%= f.submit "Save outcome", class: "btn btn-primary" %>
<% end %>
<% end %>
</ul>
<% end %>
When using symbol in form_for and fields_for Rails tries to use an instance variable with he same name, e.g. #outcomes for :outcomes. So try (for existing outcomes):
<% #outcomes = a.outcomes %>
before the line with f.fields_for :outcomes....
And for new outcomes:
<% #outcomes = a.outcomes.build %>
(the last with contribution to the owner of the question)

Controller and routes issues in my rails app

I have an app where users can create courses, and each course has_one syllabus. How could I go about configuring my courses and syllabuses (I know it's Syllabi but apparently Rails doesn't) controller, and my routes, so on a course's page there is a link to create or show the course's syllabus, and a link back to the course from the show syllabus page?
In my routes I have:
resources :courses do
resources :syllabuses
member do
put :enroll #this is so users can enroll in the course
end
end
Currently , so the course_id will be saved in the syllabus table in my courses_controller, I have:
def create_syllabus
#course = Course.find(params[:id])
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
redirect_to #syllabus, notice: "Successfully created syllabus."
else
render :new
end
end
then in my courses show page I have:
<section>
<% if (current_user.courses.includes(#course) ||
current_user.coursegroups.find_by_course_id_and_role(#course.id, "admin")) %>
<%= render 'create_syllabus' %>
<% end %>
</section>
then in my create_syllabus form (in my courses views folder) I have tried starting it off with:
# I have #course = Course.find(params[:id]) defined in show in the
#courses_controller
<%= form_for #course.create_syllabus do |f| %>
<%= form_for #course.syllabus.create_syllabus do |f| %>
<%= form_for #course.syllabus.create do |f| %>
and I get an undefined method error for each of those.
If you want to create a new syllabus in your show action of a specific course, you can add this to your controllers and views:
courses_controller.rb
#course = Course.find(params[:id])
# Build a new #syllabus object, only if there is none for the current course
unless #course.syllabus
#syllabus = #course.build_syllabus
end
views/courses/show.html.erb
# Show the syllabus name if there is one, or show the form to create a new one
<% if #course.syllabus.name %>
<p>Syllabus: <%= #course.syllabus.name %></p>
<% else %>
<p>Create Syllabus:</p>
<%= form_for([#course, #syllabus]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
syllabuses_controller.rb
def create
#course = Course.find(params[:course_id])
# Build new syllabus object based on form input
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
# redirect to /course/:id
redirect_to #course, notice: 'Syllabus was successfully created.' }
end
end
course.rb
class Course < ActiveRecord::Base
attr_accessible :name
has_one :syllabus
end
syllabus.rb
class Syllabus < ActiveRecord::Base
belongs_to :course
attr_accessible :name, :course_id
end
Some things that I left out but you should still include:
validations
rerendering form if something goes wrong
pulling things out into partials
fixing bad code like if #course.syllabus.name
pull out if/else logic into a helper
…

Rails 3 & Devise: Keeping track of who-owns-profile for user posts

My app has a user model and a post model, where user has_many posts and posts belong_to users. Posts are displayed on a user's profile page. I'd like for any user to be able to post on his own, or any other user's profile page. However, the problem I'm having is that while I know who is posting (current_user), I don't know whose profile current_user is on. I need to know this in order to assign the new post to that user's posts. How do I extract user id information from the profile currently being viewed, so I know where to assign the new post?
My micropost controller looks like:
class MicropostsController < ApplicationController
before_filter :authenticate_user!
def create
#user_of_page = User.find_by_name(params[:id])
#micropost = #user_of_page.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to :back
else
redirect_to about_path
end
end
def destroy
end
end
But I'm getting a NoMethodError: undefined method `microposts' for nil:NilClass. I assume this is because I'm making some mistake with the creation of the user_of_page variable, but I don't know what that is!
SOLUTION
Thanks Sam. I took your advice and ended up doing it like this:
I added a column to my Micropost table called belongs_to_id.
I then passed the id of the user whose profile is being shown from the user show view to the micropost controller using a hidden field in the micropost form, like so:
<%= form_for #micropost do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :content, "Why that mood?" %>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.hidden_field :author, :value => current_user.name %>
<%= f.hidden_field :belongs_to_id, :value => #user.id %>
<%= f.hidden_field :agree, :value => "0" %>
<%= f.hidden_field :disagree, :value => "0" %>
<%= f.hidden_field :amused, :value => "0" %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
I then used this id value to search for the user to assign the post to, in the micropost controller, like so:
class MicropostsController < ApplicationController
before_filter :authenticate_user!
def create
#user_of_page = User.find(params[:micropost][:belongs_to_id])
#micropost = #user_of_page.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to :back
else
redirect_to about_path
end
end
def destroy
end
end
Magic! Thanks again, you helped me to see it in the right way.
I would do it like this:
class profiles_controller < AC
...
def show
#profile = User.find(params[:id]).profile || current_user.profile
#post = Post.new
end
..
end
/profiles/show.html.erb
...
Name: <%= #profile.full_name %>
...
<%= form_for #post do |f| %>
<%= hidden_field_tag #profile.user %>
...
<% end %>
class microposts_controller < AC
def create
profile_user = User.find(params[:user_id]) # Owner of the profile current_user is on
..
end
end
Not tested. Hope this helps.

Resources