I am having trouble setting the user_id on the creation of a post
def create
#post = Post.new(post_params)
#post.user_id = session[:id]
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
The error I get is specifically undefined method 'username' for nil:NilClass when redirected to the show action. A look at the database shows that no user_id is set.
I also tried passing the user_id as a hidden field. but this did not work either (a look at the logs showed the hidden field wasn't even being passed for some reason).
Would appreciate a point in the right direction,
Thank you !
EDIT: as requested here is the show controller
def show
#post = Post.find(params[:id])
#original_id = params[:original_id]
#comment = Comment.new
end
<%= form_for [:admin, #post] do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Below is the form
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= f.text_field :category %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Routes:
root 'posts#index'
resources :posts do
resources :comments, shallow: true
end
resources :users, only: [:create]
#Posts
get "/meta", to: "posts#meta"
#User Routes
get "/signup", to: "users#new"
get "/success", to: "users#index"
#login Routes
get "/login", to: "sessions#login"
post "/sessions/login_attempt", to: "sessions#login_attempt"
#session routes
get "/sessions/home", to: "sessions#home"
get "/sessions/logout", to: "sessions#logout"
#admin routes
#resources :posts, module: 'admin', except: [:show, :index]
namespace :admin do
root 'posts#new'
resources :posts, except: [:show, :index]
end
Inside of your create method add
#post = Post.build(params[:post])
#post.user_id = current_user.id
This will grab the current user id for the given post.
If you have a has_many relationship between posts and users, why not do something like this:
#user = User.find(params[:id])
#post = #user.posts.new(params[:post])
The params might be different but this is the 'more ruby' way do do things. It explicitly notes the relationships between the two models. The above code should be in the #new action and then you would save it within the create action.
I feel like an idiot now but the issue was I was using session[:id] as opposed to session[:user_id]
Set in my sessions controller
def login_attempt
authorized_user = RegisteredUser.authenticate(params[:username_or_email],params[:login_password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = "Wow Welcome again, you logged in as #{authorized_user.username}"
redirect_to(:action => 'index', :controller => "users")
else
flash[:notice] = "Invalid Username or Password"
flash[:color]= "invalid"
render "login"
end
end
Related
I have the following post controller:
def create
#book = Book.find(params[:book_id])
#post = #book.posts.create(post_params)
if #post.save
redirect_to book_path(#book), notice: "Success!~"
else
redirect_to book_path(#book), alert: "Failure!"
end
end
Exactly same redirect_to is used for comments. Comment creation form and list are on the same url as the post creation form and list (which is show.html.erb for book)
comments controller create:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to book_path(#book), notice: "Success!~"
else
redirect_to book_path(#book), alert: "Failure!"
end
end
But when I create a comment, this error shows: No route matches {:action=>"show", :controller=>"books", :id=>nil}, missing required keys: [:id]. The comment is created and saved in the database.
I have tried book and book.id instead of #book. None worked. (interestingly, from books list to show.html.erb, I can only go there by book_path(book.id) and not book_path(#book)).
Here's my book show action, and below it is my show.html.erb for book.
#book = Book.find(params[:id])
#post = #book.posts.new
#comment = Comment.new
show.html.erb:
<%= form_for([#book, #book.posts.build]) do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit "Post"%>
</p>
<% end %>
<% #book.posts.each do |post| %>
<p>
<%= #book.title %>
<%= post.text %>
</p>
<%= form_for(post.comments.build, url: "/posts/#{post.id}/comments") do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit "Post comment"%>
</p>
<% end %>
<% end %>
routes:
resources :users do
resources :books, shallow: true
end
resources :books do
resources :posts, shallow: true
end
resources :posts do
resources :comments, shallow: true
end
The error says it all, you are not supplying an id for the book. Notice how the show route is something like /books/2 where 2 is the id? That number is not getting supplied in the comments controller. It looks to me like comments belong to a post which belongs to a book, so this should solve the issue for you.
if #comment.save
redirect_to book_path(#post.book.id), notice: "Success!~"
else
redirect_to book_path(#post.book.id), alert: "Failure!"
end
In your code you are using #book but it does not look like you ever set that variable with any values the way you do in the create method, so there is no id value there, make sense?
I'm setting up a in-app notification to show all the notifications that the user has however I am getting an can't find ID error. I am following this guide here https://www.youtube.com/watch?v=KOs8f8TnwZs.
I want to give notifications every time a Service Order gets a new Request associated with it.
Do you know what could be causing this?
I have tried going into my routes and adding something like this:
resources :service_orders do
resources :requests
end
But it didnt work, and it just made the requests route not work.
The code that I newly added is:
(#service_order.users.uniq - [current_user]).each do |user|
Notification.create(recipient: user, actor: current_user, action: "posted", notifiable: #request)
end
And thats whats causing the error, #service_order isn't being found for some weird reason.
My Request Controller is:
class RequestsController < ApplicationController
before_action :set_request, only: [:show, :edit, :update, :destroy]
before_action :old_request, only: [:show, :index, :update]
before_action :set_so
def index
if logged_in?(:site_admin)
#request = Request.all
elsif logged_in?(:itp_user)
#request = Request.pending.requests_by current_user
else
#request = Request.pending.so_user_request current_user.service_orders
end
end
def new
#request = Request.new
end
def create
#request = current_user.requests.new(request_params)
authorize #request
respond_to do |format|
if #request.save
(#service_order.users.uniq - [current_user]).each do |user|
Notification.create(recipient: user, actor: current_user, action: "posted", notifiable: #request)
end
format.html { redirect_to #service_order, notice: 'Your request has been sent' }
else
format.html { redirect_to #request, :flash => { :error => #request.errors.full_messages.join(', ') } }
end
end
end
def show
authorize #request
end
def edit
authorize #request
end
def update
authorize #request
if #request.update(request_params)
redirect_to requests_path, notice: 'Your request was created successfully'
else
render :edit
end
end
def destroy
authorize #request
#request.destroy
redirect_to requests_path, notice: 'Your request was deleted successfully'
end
private
def request_params
params.require(:request).permit(:available_date,
:message,
:charge_range,
:status,
:heading,
:service_order_id,
:user_id
)
end
def set_request
#request = Request.find(params[:id])
end
def old_request
Request.where('created_at <= :fifteen_days', fifteen_days: Time.now - 15.days).where(status: "pending").destroy_all
end
def set_so
#service_order = ServiceOrder.find(params[:service_order_id])
end
end
My routes.rb:
Rails.application.routes.draw do
resources :request_logs
namespace :admin do
resources :users
resources :issue_types
resources :requests
resources :service_orders
root to: "users#index"
end
resources :conversations do
resources :messages
end
resources :requests
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
resources :service_orders, except: [:show] do
member do
get :toggle_status
end
end
get 'user-landing', to: 'user_landing#index'
get 'service_order/:id', to: 'service_orders#show', as: 'service_order_show'
get 'contact', to: 'pages#contact'
root to: 'pages#home'
end
Request Form View:
<%= form_for :request, url: requests_path do |f| %>
<% if #request.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#request.errors.count, "error") %> prohibited this request from being sent:</h2>
<ul>
<% #request.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<h4>Subject:</h4>
<em><%= f.text_area :heading, class: "form-control" %></em>
</div>
<div class="form-group">
<h4>Message:</h4>
<em><%= f.text_area :message, class: "form-control" %></em>
</div>
<div class="form-group">
<h4>Issue Charge Range:</h4>
<em><%= f.number_field :charge_range, class: "form-control" %></em>
</div>
<div class="form-group">
<h4>Soonest Available Date:</h4>
<em><%= f.date_field :available_date, class: "form-control" %></em>
</div>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :service_order_id, :value => #service_orders.id %>
<div>
<%= f.submit 'Send Service Request', class: 'btn btn-primary btn-block' %>
</div>
<% end %>
To route to nested resources you should pass an array.
form_for( [#service_order, #request])
redirect_to([#service_order, #request])
# ... etc
This is equivalent to calling service_order_requests_path(service_order_id: #service_order.to_param). You can also use the shallow: true option to avoid nesting the member routes.
I am creating nested comments (like you find on Reddit). I am able to create parent comments, but when I try to create a child comment, it simply renders as a parent comment.
In my rails console, the "ancestry" field comes back "nil".
This is my comments controller:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
def show
#comment = Comment.find(params[:id])
end
def new
#link = Link.find(params[:link_id])
#comment = Comment.new(:parent_id => params[:parent_id])
#comments = Comment.all
end
def create
#link = Link.find(params[:link_id])
#parent = Link.find(params[:link_id]) if params[:link_id]
#parent = Comment.find(params[:comment_id]) if params[:comment_id]
#comment = #parent.comments.new(comment_params)
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #link, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to :back, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:link_id, :body, :user_id)
end
end
Here is my _comment_form partial
<%= div_for(comment) do %>
<div class="comments_wrapper clearfix">
<div class="pull-left">
<p class="lead"><%= comment.body %></p>
<p><small>Submitted <strong><%= time_ago_in_words(comment.created_at) %> ago</strong> by <%= comment.user.email %></small></p>
<div id="reply" style="display:none;">
<%= form_for [#comment = Comment.new(:parent_id => params[:parent_id])] do |f| %>
<%= f.hidden_field :parent_id %>
<%= f.text_area :body %> <br>
<%= f.submit %>
<% end %>
</div>
</div>
<div class="actions btn-group pull-right">
<button onClick="$('#reply').show()" class="btn btn-sm btn-default">Reply</button>
<% if comment.user == current_user -%>
<%= link_to 'Destroy', comment, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-sm btn-default" %>
<% end %>
</div>
</div>
<% end %>
These are my routes
Rails.application.routes.draw do
resources :comments
devise_for :users
devise_for :installs
resources :links do
member do
put "like", to: "links#upvote"
put "dislike", to: "links#downvote"
end
resources :comments
end
root to: "links#index"
end
Had this problem before; the answer is here:
Ancestry gem in Rails and Mutli Nesting
The problem with ancestry (this is why we changed back to acts_as_tree) is that you have to define all the ancestors in the ancestry column (as opposed to just the parent_id column of acts_as_tree).
Thus, when you call the .children of an object (where you've literally just populated ancestry with top-level parents) is a list of children for that parent (no others).
What you need is to reference the entire ancestry line. This is quite tricky, but can be achieved using the code below:
#app/views/links/index.html.erb
<%= render #link.comments if #post.comments.any? %>
#app/views/links/_comment.html.erb
<%= comment.title %>
<%= render "form", locals: {link: #link} %>
<%= render comment.children if comment.has_children? # > adds recursion (multi level nesting) %>
#app/views/links/_form.html.erb
<%= form_for link.comments.new do |c| %>
<%= c.text_field :body %>
<%= c.submit %>
<% end %>
The controller is as follows:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#link = Link.find params[:link_id]
#comment = #link.comments.new ancesrtry: parent(params[:parent_id])
end
private
def parent(param)
parents = Comment.find(param).pluck(:parent)
"#{parents}/#{param}" #-> ruby automatically returns last line
end
end
This should set the correct path for you, and the partials should give you the appropriate recursion required for multi level nesting.
I'd like to automatically associate a new database entry with the database entry it belongs to without having to make a choice while on the form as the user can only come from the category page, so that once you're in a category and you decide to make a new entry within that category, the newly created entry is automatically within that category upon submission. Can anyone offer any help?
My models are as follows:
class Category < ActiveRecord::Base
has_many :guides
end
class Guide < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :ratings
def average_rating
average = ratings.inject(0.0){ |sum, el| sum + el.value }.to_f / ratings.size
average.round(2)
end
end
The link to create the new guide for the category is pretty standard, though I thought that adding an instance variable might automatically associate the entry with the category though it doesn't:
<%= link_to 'New Guide', new_guide_path(#category) %>
Here is the controller for the guide:
class GuidesController < ApplicationController
before_action :set_guide, only: [:show, :edit, :update, :destroy]
# GET /guides
# GET /guides.json
def index
#guides = Guide.all
end
# GET /guides/1
# GET /guides/1.json
def show
end
# GET /guides/new
def new
#guide = Guide.new
end
# GET /guides/1/edit
def edit
end
# POST /guides
# POST /guides.json
def create
#guide = Guide.new(guide_params)
respond_to do |format|
if #guide.save
format.html { redirect_to #guide, notice: 'Guide was successfully created.' }
format.json { render :show, status: :created, location: #guide }
else
format.html { render :new }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /guides/1
# PATCH/PUT /guides/1.json
def update
respond_to do |format|
if #guide.update(guide_params)
format.html { redirect_to #guide, notice: 'Guide was successfully updated.' }
format.json { render :show, status: :ok, location: #guide }
else
format.html { render :edit }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
# DELETE /guides/1
# DELETE /guides/1.json
def destroy
#guide.destroy
respond_to do |format|
format.html { redirect_to guides_url, notice: 'Guide was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_guide
#guide = Guide.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def guide_params
params.require(:guide).permit(:name, :category_id, :user_id, :stepOneText, :stepOnePhoto, :stepTwoText, :stepTwoPhoto, :stepThreeText, :stepThreePhoto)
end
end
Form is pretty standard too, is there anything I should put in here to automatically assign it to the category entry it belongs to?
<%= form_for(#guide) do |f| %>
<% if #guide.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#guide.errors.count, "error") %> prohibited this guide from being saved:</h2>
<ul>
<% #guide.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :stepOneText %>
<%= f.text_field :stepOneText %>
</div>
<div class="field">
<%= f.label :stepOnePhoto %>
<%= f.text_field :stepOnePhoto %>
</div>
<div class="field">
<%= f.label :stepTwoText %>
<%= f.text_field :stepTwoText %>
</div>
<div class="field">
<%= f.label :stepTwoPhoto %>
<%= f.text_field :stepTwoPhoto %>
</div>
<div class="field">
<%= f.label :stepThreeText %>
<%= f.text_field :stepThreeText %>
</div>
<div class="field">
<%= f.label :stepThreePhoto %>
<%= f.text_field :stepThreePhoto %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Seems to me that you could go for something like a nested route here :
resources :categories do
resources :guides
end
and then use your new route
<%= link_to 'New Guide', new_category_guide_path(#category) %>
This should make it easier to get the guide's category back when getting the form back.
Assuming that you have a column on your guide table that stores category ID, and your routes are nested as has been recommended, you should be able to add
#guide.category_id = #category.id
To your guides controller create action. And in your form change the first line to
<%= form_for[#category, #guide] do |f| %>
Now this should work
<%= link_to 'new guide', new_category_guide_path(#category) %>
And the current category should be assigned to your guide when it's created.
I've created nested routes for a model called Userfolder. The routes are mapped like this:
userfolder_userfolders POST /userfolders/:userfolder_id/userfolders(.:format) userfolders#create
new_userfolder_userfolder GET /userfolders/:userfolder_id/userfolders/new(.:format) userfolders#new
Which is exactly the way I want it. But when I create a new Userfolder, Rails is redirecting the create action to "/userfolders" and not "/userfolders/:userfolder_id/userfolders". It is still following the initial Rails scaffolding routes.
Is there a way to change this? Or have I missed out on something entirely?
EDIT 1: Here is my Userfolder controller code.
class UserfoldersController < ApplicationController
before_action :set_userfolder, only: [:show, :edit, :update, :destroy]
before_action :set_parentfolder, except: [:show, :edit, :update, :destroy, :index]
# GET /userfolders
# GET /userfolders.json
def index
if Userfolder.first.nil?
Userfolder.create(:name => 'root', :parent_id => 0)
end
redirect_to Userfolder.first
end
# GET /userfolders/1
# GET /userfolders/1.json
def show
end
# GET /userfolders/:userfolder_id/userfolders/new(.:format)
def new
#userfolder = #parentfolder.children.build
end
# GET /userfolders/1/edit
def edit
end
# POST /userfolders/:userfolder_id/userfolders
def create
#userfolder = #parentfolder.children.build(userfolder_params)
respond_to do |format|
if #userfolder.save
format.html { redirect_to userfolder_path(#parentfolder.id), notice: 'Userfolder was successfully created.' }
else
render :action => 'new'
end
end
end
# PATCH/PUT /userfolders/1
# PATCH/PUT /userfolders/1.json
def update
respond_to do |format|
if #userfolder.update(userfolder_params)
format.html { redirect_to #userfolder, notice: 'Userfolder was successfully updated.' }
else
render :action => 'edit'
end
end
end
# DELETE /userfolders/1
# DELETE /userfolders/1.json
def destroy
parent_folder = #userfolder.parent
#userfolder.destroy
respond_to do |format|
format.html { redirect_to parent_folder, notice: 'Userfolder was successfully destroyed.' }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_userfolder
#userfolder = Userfolder.find(params[:id])
end
def set_parentfolder
#parentfolder = Userfolder.find(params[:userfolder_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def userfolder_params
params.require(:userfolder).permit(:name, :parent_id)
end
end
This is my routes.rb file:
Rails.application.routes.draw do
resources :userfiles
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'userfolders#index'
resources :userfolders, :shallow => true, :except => [:new, :create] do
resources :userfolders, :only => [:new, :create]
end
EDIT 2:
This is the console output:
So the issue was with the _form.html.erb like I suspected.
This is the create controller:
def create
#userfolder = #parentfolder.children.build(userfolder_params)
respond_to do |format|
if #userfolder.save
format.html { redirect_to #parentfolder, notice: 'Userfolder was successfully created.' }
else
render :action => 'new'
end
end
end
And here is the _form.html.erb:
<%= form_for([#parentfolder, #userfolder]) do |f| %>
<% if #userfolder.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#userfolder.errors.count, "error") %> prohibited this userfolder from being saved:</h2>
<ul>
<% #userfolder.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :parent_id %><br>
<%= f.number_field :parent_id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
By adding the <%= form_for([#parentfolder, #userfolder]) do |f| %>, I'm telling Rails to redirect to /userfolders/:userfolder_id/userfolders. Instead of redirecting to /userfolders.