So I have a self-joining model defined. Basically a post on a forum, and a parent_post that it belongs to.
class Post < ActiveRecord::Base
has_many :replies, :class_name => "Post"
belongs_to :thread, :class_name => "Post", :foreign_key => "parent_post_id"
end
Which seems fundamentally sound. I created a new RESTful route for the reply action, and an action and view.
Routes:
resources :forums do
resources :posts do
member do
get 'reply'
end
end
end
The view layer and the control action seems to be where I'm getting hosed up.
def reply
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build(params[:post])
#post.user = current_user
if #post.save
redirect_to forum_post_path(#forum, #post), notice: 'Post was successfully created.'
else
render action: "new"
end
end
And in the view layer I was just trying to use the same scaffold generated form partial I'm using for the standard new and edit actions.
#reply.html.erb
<%= render :partial => 'form' %>
#_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |f| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
<div class="form-actions">
<%= f.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
However, the parent_post_id is getting lost when I'm creating the post and it's getting set to nil. Do I need to create another action? Is there some other way to set the thread? Some third thing?
This will work:
Reply action:
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
Then add this to your view
<%= f.hidden_field :parent_post_id, #post.thread.id %>
BTW I question whether you need a custom reply method as opposed to using built-in RESTful methods but this should fix your problem and that wasn't really your question.
add
<%= hidden_field_tag :forum_id , #forum.id %>
to your form
So basically, when you're submitting to the Posts#create action you're submitting a url that looks something like this /forum/1/posts which removes the parent_post_id from the url. Since you're using that parent_post_id to build that url, you need a way to POST with it.
My suggestion is allowing a POST to a reply resource that is nested in the posts resource.
(ie POST /forums/1/posts/1/reply)
So maybe something like this
resources :forums do
resources :posts do
# :show is actually just pointing to a form
resource :reply, :only => [:show, :create],
:controller => 'reply' #otherwise gets routed to 'replies'
end
end
So you would also need a ReplyController but that would basically match your reply method on your post controller with a few changes.
def show
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build
#reply.thread = #post
#reply.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build(params[:reply])
#reply.thread = #post
#reply.user = current_user
if #reply.save
redirect_to forum_post_path(#forum, #post), notice: 'Reply was successfully created.'
else
render action: "show"
end
end
The biggest problem would be that you would have to abstract your Post fields from your form for block. That's because the url you're trying to POST to is going to be different. But it shouldn't be too bad just doing something like this:
reply/show.html.erb
<%=
form_for #reply, :url => forum_post_reply_path(#forum, #post),
:html => { :class => 'form-horizontal' } do |builder|
%>
<fieldset>
<legend><h1>New Reply</h1></legend>
<%= render "posts/post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_post_path([#forum, #post]), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |builder| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<%= render "post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_post_fields.html.erb
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
Note: There's probably a better way to declare the routes than I have, but I don't really know.
Related
I am using Rails 4.2 with Ruby 2.1.5
Here is my routes file:
Rails.application.routes.draw do
root to: "services#index"
resources :apis, only: [:new, :create]
resources :commons, path: "/self-care2/commonController" do
collection do
post :search, to: "commons#search"
end
end
end
Here is my rake routes result:
Prefix Verb URI Pattern Controller#Action
root GET / services#index
apis POST /apis(.:format) apis#create
new_api GET /apis/new(.:format) apis#new
search_commons POST /self-care2/commonController/search(.:format) commons#search
commons GET /self-care2/commonController(.:format) commons#index
POST /self-care2/commonController(.:format) commons#create
new_common GET /self-care2/commonController/new(.:format) commons#new
edit_common GET /self-care2/commonController/:id/edit(.:format) commons#edit
common GET /self-care2/commonController/:id(.:format) commons#show
PATCH /self-care2/commonController/:id(.:format) commons#update
PUT /self-care2/commonController/:id(.:format) commons#update
DELETE /self-care2/commonController/:id(.:format) commons#destroy
Here are some actions of controller:
def edit
#api = Api.find(params[:id])
end
def update
#api = Api.find(params[:id])
if #api.update(api_params)
flash[:info] = request.original_url + ".do?apiname=" + "#{#api.name}"
redirect_to root_path
else
#api.statuses.new
render :edit
end
end
def new
#api = Api.new
#status = #api.statuses.new
#status.descriptions.new
end
def create
#api = Api.new(api_params)
if #api.save
flash[:info] = request.original_url + ".do?apiname=" + "#{#api.name}"
redirect_to root_path
else
#api.statuses.new
render :new
end
end
And here is my edit template:
<%= form_for #api, :url => commons_path, :method => :patch do |f| %>
<div class="form-group">
<%= f.label :name, "API Name", class: "col-sm-2 control-label" %>
<div class="col-sm-8">
<%= f.text_field :name, class: "form-control" %>
</div>
</div>
<%= f.fields_for :statuses do |status| %>
<div class="form-group">
<%= status.label :name, "Status", class: "col-sm-2 control-label" %>
<div class="col-sm-8">
<%= status.text_field :name, class: "form-control" %>
</div>
</div>
<%= status.fields_for :descriptions do |description| %>
<div class="form-group">
<%= description.label :value, "Body", class: "col-sm-2 control-label" %>
<div class="col-sm-8">
<%= description.text_area :value, class: "form-control", rows: 12, cols: 65 %>
</div>
</div>
<% end %>
<% end %>
<%= f.submit("Edit Data", class: 'btn btn-primary col-sm-offset-2') %>
<%= link_to "Cancel", root_path, class: "btn btn-danger" %>
<% end %>
When I access to edit template, I can see old data and replace them with new data.
But show "No route matches [PATCH] "/self-care2/commonController" after I click submit button.
Anyone know what happen?
Since you update specific Api, shouldn't it be like:
<%= form_for #api, :url => common_path(#api.id), :method => :patch do |f| %>
Background: I would like to make a team and have the user verify the address of that team before the team is saved.
In my application I have a form that creates a team when the form is submitted. Within this form I have a partial that is suppose to render with a field location. When the user clicks submit within the partial form the location field (within the partial form and not the create team form) should go to the verify_Address action within the teams_controller. Instead of this happening I get an error when I load the page.
The error on pageload:
undefined local variable or method `verify_address' for #<#<Class:0x000001063ec8d8>:0x00000104555af0>
with this line highlighted: <%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
below are my files within the app.
route.rb file:
resources :users, :path => :captains, :as => :captains, :controller => "captains" do
resources :teams, :only => [:new, :create, :edit, :update, :destroy], controller: 'captains/teams'
end
resources :teams, :only => [:index, :show] do
resources :users, :path => :teammates, :as => :teammates, :controller => "teammates"
end
put 'captains/:id/teams/verify_address' => "teams#verify_address",as: 'verify_address'
get 'captains/:id/teams/verify_address' => "teams#verify_address"
controller/captains/teams_controller.rb:
class Captains::TeamsController < ApplicationController
respond_to :html, :js
def new
#team = Team.new
#captain = current_user
end
def verify_address
#address = params[:team][:location]
#validate_address = Team.validate_address(#address)
end
def create
#captain = current_user.id
#team = Team.create(
:name => params[:team][:name],
:location => params[:team][:location],
:sport => params[:team][:sport],
:captain_id => #captain,
:avatar => params[:team][:avatar]
)
if #team.present?
redirect_to #team # show view for team
end
binding.pry
end
end
the partial views/captains/teams/_verify_address.html.erb:
<%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit "Verify address" ,class: 'btn btn-success' ,id: 'verify_address' %>
</div>
<% end %>
the main form views/captains/teams/new.html.erb:
<%= form_for :team, url: captain_teams_path(#captain, #team), method: :post do |f|
%>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</div>
<div class = "form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :sport %>
<%= f.text_field :sport, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit class: 'btn btn-success' ,id: 'team_role_submit' %>
</div>
<% end %>
</div>
<%= render partial: "/captains/teams/verify_address", locals: { address: #address, validate_address: #validate_address}%>
</div>
Creating a custom route verify_address generates verify_address_path url helper, which you should use in your form.
I'm trying to add a custom create action for my Book model, but i keep ending up with a "Couldn't find Book without an ID".
routes.rb:
Books::Application.routes.draw do
resources :books
resources :books do
collection do
post 'create_new_record', :action => :create_new_record
end
end
match 'create_new_record' => 'books#create_new_record', via: [:post]
The relevant controller action:
def create_new_record
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'New book record created.' }
end
end
end
And my form (in new.html.erb). I'm looping through results that i get from goodreads.com.
<% #book_search.results.work.each do |stuff| %>
<%= form_for(#book, :url => create_new_record_books_path) do |f| %>
<div class="field">
<%= f.label :author %><br>
<%= f.text_field :author, :value => stuff.best_book.author.name %>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title, :value => stuff.best_book.title %>
</div>
<div class="field">
<%= f.label :isbn %><br>
<%= f.text_field :isbn, :value => stuff.best_book.isbn %>
</div>
<div class="field">
<%= f.label :image %><br>
<%= f.text_field :image, :value => stuff.best_book.image_url %>
</div>
<div class="field">
<%= f.label :bookid %><br>
<%= f.text_field :bookid, :value => stuff.best_book.id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
<% end %>
The error i get when submitting the form is:
ActiveRecord::RecordNotFound in BooksController#create_new_record
on the callback
def set_book
#book = Book.find(params[:id])
end
I'm pretty much stumped now, my understanding is that it doesn't even reach the action, but instead looks for a book id that doesn't exist?
Thank you!
If you use before_filter so you don't pass an id to create action. Call your before filter the following way:
before_filter :set_book, except: [:index, :new, :create]
If you use model callback, params is unavailable in the model so pass the id some other way, for example via attr_accessor.
use #book = Book.where(id: params[:id]).first
At the edit page, if I leave captcha empty, and press "save button", it takes me to #new page and says 'Wrong Captcha!'.
In fact, it should take me to edit page again.
Why my controller won't detect that the request was come from edit page?
comunity_topics_controller.rb
before_filter :simple_captcha_check, :only => [:update, :create]
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
if request.put? # We came from an edit request
#community_topic = CommunityTopic.find(params[:id])
#community_topic.attributes = params[:community_topic]
render :action => :edit
elsif request.post? # We came from a new request
#community_topic = CommunityTopic.new params[:community_topic]
render :action => :new
end
end
end
_form.html.erb
<%= form_for :community_topic, url: community_topic_index_url, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area' %>
</div>
</div>
<div class="control-group">
<div class="controls">
<%= show_simple_captcha(:label => "human authentication") %>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
community_topic_index_path, :class => 'btn' %>
</div>
<% end %>
UPDATE:
routes.rb
resources :communities, :path => "shop", do
resources :community_topics, :path => "topic", :as => :'topic'
end
New Update:
rake routes | grep community_topic
community_topic_index GET /shop/:community_id/topic(.:format) community_topics#index
POST /shop/:community_id/topic(.:format) community_topics#create
new_community_topic GET /shop/:community_id/topic/new(.:format) community_topics#new
edit_community_topic GET /shop/:community_id/topic/:id/edit(.:format) community_topics#edit
community_topic GET /shop/:community_id/topic/:id(.:format) community_topics#show
PUT /shop/:community_id/topic/:id(.:format) community_topics#update
DELETE /shop/:community_id/topic/:id(.:format) community_topics#destroy
Because the request method is post and not put. To use a put request you should write:
<%= form_for #community_topic, url: community_topics_url,
#MKK <%= form_for :community_topic, url: community_topic_url(#community_topic), :html => { :class => 'form-horizontal' } do |f| %> but check it within your rake routes
First of all, My resource is nested using to_param for slug at Community model.
I'm at example.com/shop/walmart/topic/14/edit .
If I press update without captcha input, it obviously should take me back to edit page again with flash error message.
However it takes me to example.com/shop/14/topic/14/edit . <= it's taking the same parameter. it should take 'walmart' which is community_name for first argument, and :id for topic.
All the fields are set the same with what I typed in at the previous page.
How can I avoid this? it should redirect to the same url as previous page.
routes.rb
resources :communities, :path => "shops", do
resources :community_topics, :path => "topics"
end
controller
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
if request.put? # We came from an edit request
#community_topic = CommunityTopic.find(params[:id])
#community_topic.attributes = params[:community_topic]
render :action => :edit
elsif request.post? # We came from a new request
#community_topic = CommunityTopic.new params[:community_topic]
render :action => :new
end
end
end
models/community.rb Note that I use slug here
def to_param
"#{community_name}"
end
views/community_topics/_form.html.erb
<%= form_for #community_topic, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area' %>
</div>
</div>
<div class="control-group">
<div class="controls">
<%= show_simple_captcha(:label => "human authentication") %>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
community_topic_index_path, :class => 'btn' %>
</div>
<% end %>
rake routes | grep community_topic
community_community_topics GET /shops/:community_id/topics(.:format) community_topics#index
POST /shops/:community_id/topics(.:format) community_topics#create
new_community_community_topic GET /shops/:community_id/topics/new(.:format) community_topics#new
edit_community_community_topic GET /shops/:community_id/topics/:id/edit(.:format) community_topics#edit
community_community_topic GET /shops/:community_id/topics/:id(.:format) community_topics#show
PUT /shops/:community_id/topics/:id(.:format) community_topics#update
DELETE /shops/:community_id/topics/:id(.:format) community_topics#destroy
By the way, my index action in controller is just like this, and it's working fine!
community_topics_controller.rb #index
def index
#community = Community.find_by_community_name(params[:community_id])
#community_topics = #community.community_topics
respond_to do |format|
format.html # index.html.erb
format.json { render json: #community_topics }
end
end
I don't see your controller actions, and don't know names of variables, but anyway in case of nested routes you have to define all urls precisely with named routes, or with polymorphic helper (as i do).
So your form helper must be looking as next:
<%= form_for([#community, #community_topic], :html => { :class => 'form-horizontal' }) do |f| %>
it have to send request to /shop/walmart/topic/14/update (or 'new' if #community_topic is a new record)
community.rb:
you can just
def to_param
community_name
end
routes.rb:
resources :communities, :path => "shop", do
resources :community_topics, :path => "topic"#, :as => :'topic' *
end
# * named route 'community_topic' can conflict with 'community_topics' of standalone route for CommunityTopic. Let it be by default: 'community_community_topic'.