When I submit the form, the data is not being sent in the post and set via parameters. I can't for the life of me figure out why. This form is in the plan/show action, so that's why you see me setting the #action variable there. It is sent via JS.
routes.rb
resources :plans do
resources :actions
end
action.rb
belongs_to :plan
plan.rb
has_many :actions
plans_controller.rb
def show
#plan = current_user.plans.includes(:actions).find(params[:id])
#action = Action.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #plan }
end
end
actions_controller.rb
before_filter :get_plan
def create
#action = #plan.actions.new(params[:action])
#action.user_id = current_user.id
#action.save
end
private
def get_plan
#plan = current_user.plans.find(params[:plan_id])
end
create.js.erb in views/actions folder
$('div#actions').prepend("<%= escape_javascript(render #action) %>");
$('div#<%= dom_id(#action) %>').effect('highlight');
_form.html.erb partial
<%= form_for ([#plan, #action]), remote: true do |f| %>
<%= f.text_field :desc %>
<%= f.number_field :days %>
<%= f.submit %>
<% end %>
parameters sent via POST (missing action hash - why??)
Started POST "/plans/1/actions"
Parameters: {"utf8"=>"✓", "authenticity_token"=>"**removed**", "commit"=>"Create Action", "plan_id"=>"1"}
DB Schema
create_table "plans", :force => true do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "actions", :force => true do |t|
t.string "desc"
t.integer "plan_id"
t.integer "days"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Action is a reserved word. If you call it anything else (except controller, which is also reserved) then it will work.
http://guides.rubyonrails.org/action_controller_overview.html#routing-parameters
The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values.
Related
I have this routing error when I add a consumption (after submit it fails) and I am stuck, What am I doing wrong?
A user can have several cars and for each of his car he wants to look after his gas consumptions.
I have three active record models
create_table "cars", force: :cascade do |t|
t.string "car_name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "consumption_searches", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "consumptions", force: :cascade do |t|
t.float "total_price"
t.float "kilometers"
t.string "shop"
t.float "liter_price"
t.float "total_liters"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "car_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
car belongs_to :users and has_many :consumptions
user has_many :cars, and has_many :consumptions through: :cars
consumption belong_to :car and belongs_to :user
My create method in consumptions_controller.rb
def create
#car = Car.find(params[:car_id])
#consumption = Consumptions.new(consumption_params)
#consumption.car = #car
if #consumption.save!
redirect_to car_consumptions_path, notice: 'consumption was successfully created.'
else
render :new
end
end
cars_controller.rb
def show
#car = Car.find(params[:id])
#search = ConsumptionSearch.new(params[:search])
#consumptions = #search.date_range
#consumptions = #consumptions.order('created_at ASC').where(car_id: #car.id)
end
views/consumptions/_form.html.erb
<%= simple_form_for car_consumptions_path do |f| %>
...
<% end %>
routes.rb
Rails.application.routes.draw do
devise_for :users
root to: "cars#index"
resources :cars do
resources :consumptions
end
end
rails routes | grep consumption
car_consumptions GET /cars/:car_id/consumptions(.:format) consumptions#index
POST /cars/:car_id/consumptions(.:format) consumptions#create
new_car_consumption GET /cars/:car_id/consumptions/new(.:format) consumptions#new
edit_car_consumption GET /cars/:car_id/consumptions/:id/edit(.:format) consumptions#edit
car_consumption GET /cars/:car_id/consumptions/:id(.:format) consumptions#show
PATCH /cars/:car_id/consumptions/:id(.:format) consumptions#update
PUT /cars/:car_id/consumptions/:id(.:format) consumptions#update
DELETE /cars/:car_id/consumptions/:id(.:format) consumptions#destroy
As requested
EDIT
here is what I have in the HTML if it can help:
<form novalidate="novalidate" class="simple_form /cars/1/consumptions" action="/cars/1/consumptions/new" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="0nwq/pQSXCU2ptBjbewTCBffPLpZZUPAj6/HPQTGtYd8cHz9zv8R/C/JYnXPDpKw5o3/vGlVtav2Sa2nSvgOQdQ==">
Lets start with the controller
# GET /cars/:car_id/consumptions/new
def new
#car = Car.find(params[:car_id])
#consumption = #car.consumptions.new
end
# POST /cars/:car_id/consumptions
def create
#car = Car.find(params[:car_id])
#consumption = #car.consumptions.new(consumption_params)
# `.save!` will raise an exception and blow up if the record is invalid.
# Not good.
if #consumption.save
redirect_to car_consumptions_path(#car), notice: 'consumption was successfully created.'
else
render :new
end
end
Note the redirect:
# consumptions#index
redirect_to car_consumptions_path(#car), notice: 'consumption was successfully created.'
Since this is a nested route you need to provide the car_id segment.
You could also redirect to:
# consumptions#show
redirect_to [#car, #consumption], notice: 'consumption was successfully created.'
# or to cars#show
redirect_to #car, notice: 'consumption was successfully created.'
When using simple_form_for you pass it model instances that it binds the form to. When creating forms for nested routes you should pass an array:
<%= simple_form_for([#car, #consumption]) do |f| %>
<% end %>
This uses the polymorphic route helpers to find the correct path. You can use this same signature for link_to, redirect_to, url_for and path_for.
When declaring nested routes you should consider using the shallow option. It will only nest the collection routes (new, create, index) and not the member routes.
Try this:
<%= form_for [#cars, #consumptions] do |form| %>
...
<% end %>
or
<%= form_with(model: [ #cars, #consumptions ]) do |form| %>
*** Update:
resources :cars, shallow: true do
resources :consumptions
end
Into form:
<% = simple_form_for [#car, #consumption] do |f| %>
You are not showing your new action, but I assume you are setting a #car and a #consumption = #car.consumptions.build variables.
Try with this:
simple_form_for #consumption, url: url_for(controller: :consumptions, action: :create, car_id: #car.id) do |f|
It should work with simple_form_for [#car, #consumption] do |f| but you said "it doesn't work" which is too ambiguous (how does that not work? same error? new error? you should be more clear when responding to answers)
car_consumptions_path != car_consumption_path
I don't see any "car_consumptions" in your routes.
I'm trying to increment new values to an array but the old values get deleted. As you can see in the following, I had one image there and now its NULL, but the new image is there.
SQL (1.5ms) UPDATE "attachments" SET "media_files" = $1, "updated_at" = $2 WHERE "attachments"."id" = $3 [["media_files", "{NULL,image4.jpg}"], ["updated_at", "2018-10-25 09:12:05.564281"], ["id", 11]]
I'm using the carrierwave gem and this is the method I have inside the controller in order to keep the existing values and increment the new ones:
def create
files = #attachment.media_files # copy the old images
files += params[:item][:media_files] # add new file to the files
#attachment.assign_attributes(:media_files => files) # assign back
if #attachment.save
flash[:notice] = "Media files where successfully uploaded"
redirect_back fallback_location: root_path
else
flash[:alert] = "Failed to upload media files"
redirect_back fallback_location: root_path
end
end
And the form is:
<%= form_for #item, url: create_image_path(#attachment), method: :post , :html => {:id => "form"} do |f| %>
<%= f.file_field :media_files, multiple: true %>
<%= f.submit 'Add' %>
<% end %>
Models associations:
class Item < ApplicationRecord
has_many :attachments, dependent: :destroy
accepts_nested_attributes_for :attachments, allow_destroy: true
end
class Attachment < ApplicationRecord
belongs_to :item
mount_uploaders :media_files, AttachmentUploader
validates_presence_of :media_files
end
The schema for the two models:
create_table "attachments", force: :cascade do |t|
t.integer "item_id"
t.integer "account_id"
t.string "media_files", default: [], array: true
t.string "content_type"
t.boolean "success"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "title"
t.string "description"
t.integer "category_id"
t.integer "account_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The route for the create method:
post "item/:id/uploads/media_files/:id"=> "attachments#create", :as => :create_image
Any idea what I might be missing here?
You can go with your own code the modification need to done is: -
def create
exitsting_files = #attachment.media_files # copy the old images
new_file = params[:item][:media_files] # new file to the files
new_attachment = Aattachment.new(:media_files => new_file, id: #item.id) #New attachment submitted by form
if new_attachment.save
#push back your exitsting_files along with new one
#attachment.media_files.push(exitsting_files)
#attachment.save
flash[:notice] = "Media files where successfully uploaded"
redirect_back fallback_location: root_path
else
flash[:alert] = "Failed to upload media files"
redirect_back fallback_location: root_path
end
end
However this should be done with nested_atrributes_for concept
<%= form_for #item, url: create_image_path(#attachment), method: :post , :html => {:id => "form"} do |f| %>
<%= f.fields_for : attachments do |ff| %>
<%= ff.file_field :media_files, multiple: true %>
<%end%>
<%= f.submit 'Add' %>
<% end %>
And so on... you can get lots of example for nested form
I have a form with a select_tag and options_from_collection_for_select that I can't seem to get to pass. In the view, when the upload id is set to uploadzip_id I get a 302 redirect and when it's set to uploadzip_ids, I get a Unknown Attribute error.
I'm a bit confused as I have my relationship set up along with the foreign key. I do have another model with checkboxes called Uploadpdf that works fine.
Here is the set up..
class Campaign < ActiveRecord::Base
has_one :uploadzip
end
class Uploadzip < ActiveRecord::Base
belongs_to :campaign
end
db/schema.rb
create_table "campaigns", force: :cascade do |t|
t.string "name"
t.text "comment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
create_table "uploadzips", force: :cascade do |t|
t.string "file_name"
t.string "file_type"
t.datetime "date"
t.integer "size"
t.integer "pages"
t.string "file_ident"
t.string "md5"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "campaign_id"
end
add_foreign_key "uploadzips", "campaigns"
app/controllers/campaign_controller.rb
class CampaignsController < ApplicationController
def index
#campaigns = Campaign.all.order("created_at DESC")
end
def new
#campaign = Campaign.new
end
def create
#campaign = Campaign.new(campaign_params)
if #campaign.save
flash[:success] = "Campaign Successfully Launched!"
redirect_to #campaign
else
flash[:error] = "There was a problem launching your Campaign."
redirect_to new_campaign_path
end
end
.....
private
def campaign_params
params.require(:campaign).permit(:name, :comment, :uploadzip_ids, uploadpdf_ids: [])
end
end
views/campaigns/_form.rb
<%= form_for #campaign, url: {action: "create"} do |f| %>
.....some typical fields..
<%= f.label :data_file, class: "right-label" %>
<%= select_tag campaign[uploadzip_ids],
options_from_collection_for_select(
Uploadzip.all, :id, :file_name
), { include_blank: "Include a Zip File" } %>
.....some more typical fields
<% end %>
Update
I have changed the code to better reflect the foreign key as suggested. Creating a campaign is now successful but it's not associating with the chosen uploadzip Zip file selected. When calling #campaign.uploadzip, it returns nil.
Here is the updated code:
<%= select_tag "uploadzip[campaign_id]",
options_from_collection_for_select(
Uploadzip.all, :id, :file_name
), { include_blank: "Include a Zip File" } %>
I also changed the controller params.require to..
def campaign_params
params.require(:campaign).permit(:name, :comment, :campaign_id, uploadpdf_ids: [])
end
As per your association set-up,the foreign_key should be campaign_id not uploadzip_id. You should either change your associations or foreign_key according to your use-case.
And also I recommend you to follow these Guides to know more about associations.
The 302 redirect may not be a bad thing, since you're doing a redirect_to new_campaign_path. Are the records created correctly when you use uploadzip_id in both the view and controller params.permit section?
Member of a FaceBook group helped me figure it out by adding a little
extra logic in the controller..
if #campaign.save
zip = Uploadzip.find(params[:uploadzip_id])
zip.campaign = #campaign
zip.save
flash[:success] = "Campaign Successfully Launched!"
redirect_to #campaign
else
flash[:error] = "There was a problem launching your Campaign."
redirect_to new_campaign_path
end
..which was met with changing the select_tag's name.
<%= select_tag :uploadzip_id,
options_from_collection_for_select(
Uploadzip.all, :id, :file_name
), { include_blank: "Include a Zip File" } %>
I've been following the tutorial on creating a Rails blog (http://www.roberthuberdeau.com/articles/4-How-to-create-a-blog-in-Ruby-on-Rails-3) and have basically got all the way to the end.
However, after all the migrations I'm now struggling. Whenever I complete the form I previously used to create articles, I can't see them on the index page. I've dug around and believe the root of the error is that I'm not saving any articles when I press 'create article'.
To test this, I created an article using the console and this is appearing as it should so I think the problem lies somewhere between the form that creates articles and the controller (though am happy to be corrected on this).
Whenever I try the following appears in the logs:
Started POST "/articles" for 127.0.0.1 at 2013-04-01 21:12:58 +0100
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XLeHm+4Tgd6n9vt4RxAQ5YVTbWTi+UnqkmBso9Iuo+4=", "article"=>{"title"=>"I rule", "body"=>"Change teams.", "tag_names"=>"kill", "published"=>"1"}, "commit"=>"Create Article"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Role Load (0.1ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Admin' LIMIT 1
Role Load (0.1ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Moderator' LIMIT 1
Role Load (0.1ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Member' LIMIT 1
Redirected to http://localhost:3000/
Completed 302 Found in 5ms (ActiveRecord: 0.5ms)
With the database schema:
ActiveRecord::Schema.define(:version => 20130401171646) do
create_table "articles", :force => true do |t|
t.string "title"
t.text "body"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "user_id", :null => false
t.boolean "published", :default => false
end
create_table "comments", :force => true do |t|
t.integer "article_id"
t.string "name"
t.string "email"
t.text "body"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "roles", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "roles_users", :id => false, :force => true do |t|
t.integer "role_id"
t.integer "user_id"
end
create_table "taggings", :force => true do |t|
t.integer "article_id"
t.integer "tag_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "tags", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
end
Articles controller:
class ArticlesController < ApplicationController
before_filter :authenticate_user!, :except => [:index, :show]
# GET /articles
# GET /articles.xml
def index
#articles = Article.published.page(params[:page]).per(5).ordered
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #articles }
end
end
# GET /articles/1
# GET /articles/1.xml
def show
#article = Article.find(params[:id])
#comment = Comment.new(:article=>#article)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #article }
end
end
# GET /articles/new
# GET /articles/new.xml
def new
#article = Article.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #article }
end
end
# GET /articles/1/edit
def edit
#article = Article.find(params[:id])
authorize! :edit, #article
end
# POST /articles
# POST /articles.xml
def create
authorize! :create, #article
#article = Article.new(params[:article])
#article.user_id = current_user.id
respond_to do |format|
if #article.save
format.html { redirect_to(#article, :notice => 'Article was successfully created.') }
format.xml { render :xml => #article, :status => :created, :location => #article }
else
format.html { render :action => "new" }
format.xml { render :xml => #article.errors, :status => :unprocessable_entity }
end
end
end
# PUT /articles/1
# PUT /articles/1.xml
def update
#article = Article.find(params[:id])
authorize! :update, #article
respond_to do |format|
if #article.update_attributes(params[:article])
format.html { redirect_to(#article, :notice => 'Article was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #article.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /articles/1
# DELETE /articles/1.xml
def destroy
#article = Article.find(params[:id])
authorize! :destroy, #article
#article.destroy
respond_to do |format|
format.html { redirect_to(articles_url) }
format.xml { head :ok }
end
end
end
Articles model:
class Article < ActiveRecord::Base
attr_accessible :body, :title, :tag_names
has_many :comments, :dependent => :destroy
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
validates_presence_of :title, :body
validates_uniqueness_of :title
attr_writer :tag_names
after_save :assign_tags
validates_numericality_of :user_id
belongs_to :user
scope :published, lambda {{:conditions => ['published = ?', true]}}
scope :ordered, lambda {{:order => "Created_at DESC" }}
def tag_names
#tag_names || tags.map(&:name).join(' ')
end
private
def assign_tags
if #tag_names
self.tags = #tag_names.split(/\,/).map do |name|
Tag.find_or_create_by_name(name)
end
end
end
end
And the form partial used to create the article:
<%= form_for(#article) do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="field">
<%= f.label :tag_names, "Tags" %>
<%= f.text_field :tag_names %>
</div>
<div class="field">
<%= check_box("article", "published" ) %>
<%= "Publish article" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any help you can give me on this would be greatly appreciated.
By request:
Ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :Admin
can :manage, :all
can :publish, Article
elsif user.role? :Moderator
can :read, [Article, Comment]
can [:edit, :update], Comment
elsif user.role? :Member
can :read, :all
can :create, [Article, Comment]
can [:edit, :update], Comment
end
end
end
p.s. the only other error I can see (and I don't know if it's relevant or a different issue entirely) is that when trying to view an article (show.html.erb) I receive the following error:
Processing by ArticlesController#show as HTML
Parameters: {"id"=>"1"}
Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT 1 [["id", "1"]]
Completed 500 Internal Server Error in 44ms
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: article):
app/controllers/articles_controller.rb:18:in `new'
app/controllers/articles_controller.rb:18:in `show'
I'm guessing there's a problem with the authorize! :create, #article line in the ArticleController.create. At the point that that executes, #article isn't created yet.
Judging by the CanCan source, I think the following might do what you want:
def create
authorize! :create, Article
#article = Article.new(params[:article])
#article.user_id = current_user.id
...
The article is not being created because of this.
authorize! :create, #article
You will have to show us your ability model, ability.rb.
You might also want to try the obvious. bundle install and restart the server.
I have three models: User, Post and, Reply. An user has many posts and comments. A post had many replies and belongs to an user, and a reply belongs to a post and an user.
routes.rb:
resources :posts do
resources :replies
end
schema.rb:
create_table "posts", :force => true do |t|
t.text "content", :limit => 255
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "title"
end
create_table "replies", :force => true do |t|
t.text "content"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
This is how I'm creating comments:
comments_controller.rb:
def create
#post = Post.find(params[:post_id])
#reply = #post.replies.build(params[:reply])
if #reply.save!
flash[:success] = "reply created!"
redirect_to post_path(#post)
else
redirect_to post_path(#post)
end
end
replies/_form.html.erb:
<%= form_for([#post, #post.replies.build]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Enter reply content" %>
</div>
<%= f.submit "Reply", class: "btn btn-large btn-primary" %>
<% end %>
After submitting the form I get this error:
Validation failed: User can't be blank
I assume its because the reply's attribute user_id is empty:
reply.rb
validates :user_id, presence: true
I'm not sure how to fill that attribute. I can't just put it in Reply attr_accesible, because that would compromise the security of the app (as far as I know).
Any suggestions to solve this?
attr_acessible only affects things when you're updating/creating a record from a hash of attributes. You can always set an attribute by calling the accessor directly, so after you've built the reply,
#reply.user = current_user
Should do the trick (assuming you're using something like devise or authlogic that defined current_user for you. You could also assign to #reply.user_id directly.