I have a controller 'news_controller.rb' for interfacing with a database. When I try to create a database row item (I do it by an automated script making post requests), I get the following error
ActionController::ParameterMissing (param is missing or the value is empty: news): app/controllers/api/news_controller.rb:57:in `news_params'app/controllers/api/news_controller.rb:28:in `create'
When I use params[:category], params[:headline] etc., inside the create action, then it works fine. What are the possible causes and solutions for this?
Here's my controller code:
module Api
class NewsController < Api::ApiController
respond_to :json
def index
#news = News.all
respond_with :news => #news
end
def show
#news = News.find(params[:id])
respond_with :news => #news
end
def new
#news = News.new
respond_with :news => #news
end
def edit
#news = News.find(params[:id])
respond_with :news => #news
end
def create
if !News.exists?(headline: params[:headline])
##news = News.new(:category => params[:category], :headline => params[:headline], :content => params[:content], :image_url => params[:image_url])
#news = News.new(news_params)
if #news.save
respond_with :news => #news
else
respond_with #news.errors
end
end
end
def update
#news = News.find(params[:id])
if #news.update(news_params)
respond_with :news => #news
else
respond_with #news.errors
end
end
def destroy
#news = News.find(params[:id])
#news.destroy
respond_to do |format|
format.json { head :no_content }
end
end
private
def news_params
params.require(:news).permit(:category, :headline, :content, :image_url)
end
end
end
Your params has value
params = { category: 'value', headline: 'value', .... }
When you use news_params you expect your params to be formatted like
params = { news: { category: 'value', headline: 'value', .... }}
But that is not the case.
This is why it works when you use
#news = News.new(:category => params[:category], :headline => params[:headline], :content => params[:content], :image_url => params[:image_url])
And does not work with
#news = News.new(news_params)
# news_params is nil
Related
When I want to go to "localhost:3000/blog" the web page gives this error...
Showing C:/Sites/ifurniture/app/views/refinery/blog/posts/index.html.erb where line #3 raised:
undefined method `to_sym' for {:title=>"Body", :slug=>"body"}:Hash
Rails.root: C:/Sites/ifurniture
this is the blog controller..
module Refinery
module Blog
class PostsController < BlogController
before_filter :find_all_blog_posts, :except => [:archive]
before_filter :find_blog_post, :only => [:show, :comment, :update_nav]
before_filter :find_tags
respond_to :html, :js, :rss
def index
if request.format.rss?
#posts = if params["max_results"].present?
# limit rss feed for services (like feedburner) who have max size
Post.recent(params["max_results"])
else
Post.newest_first.live.includes(:comments, :categories)
end
end
respond_with (#posts) do |format|
format.html
format.rss { render :layout => false }
end
end
def show
#comment = Comment.new
#canonical = refinery.url_for(:locale => Refinery::I18n.current_frontend_locale) if canonical?
#post.increment!(:access_count, 1)
respond_with (#post) do |format|
format.html { present(#post) }
format.js { render :partial => 'post', :layout => false }
end
end
def comment
#comment = #post.comments.create(comment_params)
if #comment.valid?
if Comment::Moderation.enabled? or #comment.ham?
begin
CommentMailer.notification(#comment, request).deliver_now
rescue
logger.warn "There was an error delivering a blog comment notification.\n#{$!}\n"
end
end
if Comment::Moderation.enabled?
flash[:notice] = t('thank_you_moderated', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id])
else
flash[:notice] = t('thank_you', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id],
:anchor => "comment-#{#comment.to_param}")
end
else
render :show
end
end
def archive
if params[:month].present?
date = "#{params[:month]}/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%B %Y')
#posts = Post.live.by_month(archive_date).page(params[:page])
else
date = "01/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%Y')
#posts = Post.live.by_year(archive_date).page(params[:page])
end
respond_with (#posts)
end
def tagged
#tag = ActsAsTaggableOn::Tag.find(params[:tag_id])
#tag_name = #tag.name
#posts = Post.live.tagged_with(#tag_name).page(params[:page])
end
private
def comment_params
params.require(:comment).permit(:name, :email, :message)
end
protected
def canonical?
Refinery::I18n.default_frontend_locale != Refinery::I18n.current_frontend_locale
end
end
end
end
the post controller...
module Refinery
module Blog
class PostsController < BlogController
before_filter :find_all_blog_posts, :except => [:archive]
before_filter :find_blog_post, :only => [:show, :comment, :update_nav]
before_filter :find_tags
respond_to :html, :js, :rss
def index
if request.format.rss?
#posts = if params["max_results"].present?
# limit rss feed for services (like feedburner) who have max size
Post.recent(params["max_results"])
else
Post.newest_first.live.includes(:comments, :categories)
end
end
respond_with (#posts) do |format|
format.html
format.rss { render :layout => false }
end
end
def show
#comment = Comment.new
#canonical = refinery.url_for(:locale => Refinery::I18n.current_frontend_locale) if canonical?
#post.increment!(:access_count, 1)
respond_with (#post) do |format|
format.html { present(#post) }
format.js { render :partial => 'post', :layout => false }
end
end
def comment
#comment = #post.comments.create(comment_params)
if #comment.valid?
if Comment::Moderation.enabled? or #comment.ham?
begin
CommentMailer.notification(#comment, request).deliver_now
rescue
logger.warn "There was an error delivering a blog comment notification.\n#{$!}\n"
end
end
if Comment::Moderation.enabled?
flash[:notice] = t('thank_you_moderated', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id])
else
flash[:notice] = t('thank_you', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id],
:anchor => "comment-#{#comment.to_param}")
end
else
render :show
end
end
def archive
if params[:month].present?
date = "#{params[:month]}/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%B %Y')
#posts = Post.live.by_month(archive_date).page(params[:page])
else
date = "01/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%Y')
#posts = Post.live.by_year(archive_date).page(params[:page])
end
respond_with (#posts)
end
def tagged
#tag = ActsAsTaggableOn::Tag.find(params[:tag_id])
#tag_name = #tag.name
#posts = Post.live.tagged_with(#tag_name).page(params[:page])
end
private
def comment_params
params.require(:comment).permit(:name, :email, :message)
end
protected
def canonical?
Refinery::I18n.default_frontend_locale != Refinery::I18n.current_frontend_locale
end
end
end
end
and this is the index.html.erb of Blog.
<section class="container">
<% content_for :body do %>
<%= raw #page.content_for(Refinery::Pages.default_parts.first.to_sym) if Refinery::Pages.default_parts.any? %>
<% if #posts.any? %>
<section id="blog_posts" class="news">
<%= render :partial => "/refinery/blog/shared/post", :collection => #posts %>
<%= will_paginate #posts %>
</section>
<% else %>
<p><%= t('.no_blog_articles_yet') %></p>
<% end %>
<% end %>
<% content_for :side_body_prepend do -%>
<%= raw #page.content_for(Refinery::Pages.default_parts.second.to_sym) %>
<% end if Refinery::Pages.default_parts.many? -%>
<%= render "/refinery/content_page" %>
<% content_for :stylesheets, stylesheet_link_tag('refinery/blog/frontend') %>
</section>
I'll be watching for your help, thanks.
You can use to_sym method on hash or string so in your code you can do something like this:
index.html
instead this:
<%= raw #page.content_for(Refinery::Pages.default_parts.first.to_sym) if Refinery::Pages.default_parts.any? %>
put this:
<%= raw #page.content_for(Refinery::Pages.default_parts.first[:title].to_sym) if Refinery::Pages.default_parts.any? %>
instead [:title]you can also use [:slug]
I have a form on a cart page to update line_items. So the resource I am updating needs to exit the cart controller and enter the line_items controller... I am using the following form...
<%= simple_form_for shifted_commerce_line_item_path(item),
:url => {controller: 'line_items', action: 'update'} do |f| %>
I get no route matches...
No route matches {:action=>"update", :controller=>"shifted_commerce/line_items"}
The thing is... I have the the action in the controller...
I'll post my routes.
shifted_commerce_line_items GET /shifted_commerce/line_items(.:format) shifted_commerce/line_items#index
POST /shifted_commerce/line_items(.:format) shifted_commerce/line_items#create
new_shifted_commerce_line_item GET /shifted_commerce/line_items/new(.:format) shifted_commerce/line_items#new
edit_shifted_commerce_line_item GET /shifted_commerce/line_items/:id/edit(.:format) shifted_commerce/line_items#edit
shifted_commerce_line_item GET /shifted_commerce/line_items/:id(.:format) shifted_commerce/line_items#show
PATCH /shifted_commerce/line_items/:id(.:format) shifted_commerce/line_items#update
PUT /shifted_commerce/line_items/:id(.:format) shifted_commerce/line_items#update
DELETE /shifted_commerce/line_items/:id(.:format) shifted_commerce/line_items#destroy
How do I get this form to hit the update action in my controller?
Don't we see the above route? both PUT and PATCH match /shifted_commerce/Line_items/:id...and I'm passing the id with the (item)...
Update 1 I'll also add that I'm running this form in a loop, so every line_item can be edited. #anything isn't what I'd want to pass to the path for an ID... also, I'm operating from the carts controller, in the show action. Not a line_item controller, edit action.
Update 2 If I do the following simple_form_for:
<%= simple_form_for shifted_commerce_line_item_path(item), :url => {controller: 'line_items', action: 'create'} do |f| %>
It successfully takes me into the create action of the line_items controller. But if I do
<%= simple_form_for shifted_commerce_line_item_path(item), :url => {controller: 'line_items', action: 'update'} do |f| %>
I get the route error. What's the deal?! Why does it work for create but not for update? I have the update action in my controller...
ShiftedCommerce::LineItemsController
class ShiftedCommerce::LineItemsController < ApplicationController
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
before_action :set_line_item_item, only: [:show, :edit, :update, :destroy]
# GET /line_items
# GET /line_items.json
def index
#line_items = ShiftedCommerce::LineItem.all
end
# GET /line_items/1
# GET /line_items/1.json
def show
end
# GET /line_items/new
def new
#line_item = ShiftedCommerce::LineItem.new
end
# GET /line_items/1/edit
def edit
end
# POST /line_items
# POST /line_items.json
def create
#cart = current_cart
# item is built from base_item, after finding associated product
#base_item_id = params[:shifted_commerce_line_item][:base_item_id]
get_item_id_from_base_item_params
build_line_item
## Does a line item with the same itemt_id already exist in cart?
if #line_item.exists_in_collect?(current_cart.line_items)
#if so, change quantity, check if there's enough stock
if current_cart.where_line_item_with(#item_id).update_quantity(#line_item.quantity) == true
#line_item.destroy
redirect_to shifted_commerce_base_items_path
flash[:success] = "Detected item In Cart, Added Your Amount More to Quantity"
else #otherwise there's not enough product in stock.
redirect_to shifted_commerce_cart_path
flash[:failure] = "Cannot Add To Cart, Not Enough In Stock"
end
else # This product isn't in the cart already let's save it!
if #line_item.have_enough_item? == true # if there is enough stock, save
if #line_item.save
respond_to do |format|
format.html { redirect_to shifted_commerce_base_items_path,
:notice => "Added to Cart." }
format.xml { render :xml => #line_item,
:status => :created, :location => #line_item }
end
else
format.xml { render :xml => #line_item.errors,
:status => :unprocessable_entity }
end
else # not enough stock, not saved.
redirect_to shifted_commerce_base_items_path
if #line_item.item.stock_qty > 0
flash[:failure] = "Sorry! We Only Don't Have Enough In Stock"
else
flash[:failure] = "Sorry! That Item Is Out Stock"
end
end
end
end
# PATCH/PUT /line_items/1
# PATCH/PUT /line_items/1.json
def update
#line_item = ShiftedCommerce::LineItem.find(params[:id])
raise
#line_item.attributes = line_item_params
if #line_item.over_order_cap? #is the quantity over maximum?
flash[:error] = "Contact Us For Orders of This Size -- Quantity Set To Max"
redirect_to cart_path
else # quantity to change to not over maximum.
if #line_item.have_enough_item? == true
#line_item.update(line_item_params)
#did they update quantity to 0?
if #line_item.quantity <= 0
#line_item.destroy
flash[:success] = "Item Removed"
redirect_to :back
else
flash[:success] = "Itemed Updated"
redirect_to cart_path
end
else
redirect_to cart_path
flash[:failure] = "We Don't Have Enough In Stock. Update Failed"
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
#line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_line_item
#line_item = LineItem.find(params[:id])
end
def set_line_item_item
#line_item_name = #line_item.item.base_item
end
# Never trust parameters from the scary internet, only allow the white list through.
def line_item_params
params.fetch(:shifted_commerce_line_item, {}).permit(:item_id, :cart_id, :order_id, :quantity, :weight, :units)
end
def build_line_item
#line_item = #cart.line_items.build(
:item_id => #item_id,
:order_id => nil,
:weight => params[:shifted_commerce_line_item][:weight],
:quantity => params[:shifted_commerce_line_item][:quantity]
)
end
def get_item_id_from_base_item_params
#item_id = ShiftedCommerce::Item.where(:base_item_id => #base_item_id).where(:size_id => params[:shifted_commerce_line_item][:size]).first.id
end
end
line_item.rb
class ShiftedCommerce::LineItem < ActiveRecord::Base
belongs_to :item
belongs_to :cart
after_create :set_order_weight#, :set_package_dimentions
after_update :set_order_weight#, :set_package_dimentions
#max capactiy here
def have_enough_item?
if self.item.stock_qty >= self.quantity
return true
else
if self.quantity_was == nil
return false
else
self.quantity = self.quantity_was
self.save
return false
end
end
end
def set_order_weight
if
self.cart.nil?
else
self.cart.total_weight = self.cart.line_items.to_a.sum {|item| (item.weight)*(item.quantity)}
self.cart.save
end
end
def over_order_cap?
if self.quantity > 5
self.quantity = 5
self.save
return true
end
return false
end
def update_quantity(qty)
self.quantity += qty
if self.have_enough_item? == true
self.save
end
end
def exists_in_collect?(items)
items.each do |item|
return true if self.item_id == item.item_id
end
return false
end
def set_order_total_units
if
self.cart.nil?
else
self.set_cart_units
self.cart.total_units = self.cart.total_units
self.cart.save
end
end
def total_price
item.base_item.price * quantity
end
end
Routes.rb
namespace :shifted_commerce do
resources :line_items
resources :items
resources :base_items
resource :cart, only: [:show, :update, :destroy] do
resource :order, only: [:show, :create, :update, :edit, :new]
end
end
Change this and try:
<%= simple_form_for item, url: shifted_commerce_line_items_path(item), method: :put do |f| %>
as per you routes
Update:
Sorry it must be post but not put like this:
<%= simple_form_for item, url: shifted_commerce_line_item_path(item), method: :post do |f| %>
Try using <%= simple_form_for ([:shifted_commerce, item]) do |f| %> or
<%= simple_form_for item, :url => shifted_commerce_line_item_path do |f| %>
I have an index action in my controller that I use to render all posts as JSON:
def index
#user = current_user
#posts = Post.all
end
respond_to do |format|
format.html
format.json {
render json: #posts.to_json(:include => {
:user => { :only => [:first_name, :last_name]},
:category => { :only => [:name, :id]}
}),
:callback => params[:callback]
}
end
end
What I'd like to do, is add an additional attribute to each post's JSON output called posted_on, that has a value of: distance_of_time_in_words(post.created_at, Time.now)}
I can't seem to be able to figure out how to approach this. Any help would be appreciated!
Add the method posted_on to Post model
class Post < ActiveRecord::Base
include ActionView::Helpers::DateHelper
def posted_on
distance_of_time_in_words(created_at, Time.now)
end
end
In your controller, pass the methods option to to_json
def index
#user = current_user
#posts = Post.all
end
respond_to do |format|
format.html
format.json {
render json: #posts.to_json(:include => {
:user => { :only => [:first_name, :last_name]},
:category => { :only => [:name, :id]}
}, :methods => :posted_on),
:callback => params[:callback]
}
end
end
I have the following methods for Create and Update in my Controller:
def new
if request.post?
#article = Article.new(article_params)
#article.user = #user
if #article.save
redirect_to :admin_articles, :flash => { success: t(:article_created) }
end
else
#article = Article.new
end
end
def edit
if request.patch?
if #article.update(article_params)
redirect_to :admin_articles, :flash => { success: t(:article_updated) }
end
end
end
And I have the following for the article_params:
def article_params
article_params = params[:article].permit(:category_id, :title, :slug, :article_type, :content, :link, :summary)
if params[:tags].present?
tags = params[:tags].split ','
tags_array = Array.new
tags.each do |t|
tags_array.append Tag.find_or_create_by slug: t
end
article_params[:tags] = tags_array
end
article_params
end
When I do the Update it saves properly, but when I try to do the Create it says Article Tags is invalid. Does anyone know what I am doing wrong?
You don't have (or at least haven't shown) a create method in your controller, so you're just getting ActiveModel's default implementation which isn't going to pick up any of the params. If you're doing something non-standard with your routes, such that POST is getting mapped to new, please share that.
the issue was me not understanding convention. My object had not been created therefore it did not have a tags property yet. I have changed my methods to the following:
def article_params
params[:article].permit(:category_id, :title, :slug, :article_type, :content, :link, :summary)
end
def tags
tags = Array.new
if params[:tags].present?
tag_param_array = params[:tags].split ','
tag_param_array.each do |t|
tags.append Tag.find_or_create_by slug: t
end
end
tags
end
def new
#article = Article.new
end
def create
#article = Article.create article_params
if #article.valid?
#article.tags = tags
redirect_to :admin_articles, :flash => { :success => t(:article_created) } if #article.save
else
render 'new'
end
end
def edit
end
def patch
#article.update_attributes article_params
if #article.valid?
#article.tags = tags
redirect_to :admin_articles, :flash => { :success => t(:article_updated) } if #article.save
end
end
I"m trying to use a named scope to set my users carrier but keep getting this error
undefined method `target' for #<ActiveRecord::Relation:0x5ee3e40>
app/models/user.rb:19:in `set_carrier'
app/controllers/users_controller.rb:49:in `block in create'
app/controllers/users_controller.rb:48:in `create'
<div class= "field">
<%= f.label :carrier %><br>
<%= f.collection_select(:carrier_name, Carrier.find(:all) ,:name, :name) %>
</div>
user.rb
class User < ActiveRecord::Base
acts_as_authentic
after_create :set_sub
after_create :set_universal
after_create :set_carrier
def set_sub
#self.roles << "subscriber"
self.roles_mask = 4
end
def set_universal
self.channels << Channel.find(1)
end
def set_carrier
#carrier = Carrier.with_name(self.carrier_name)
self.carrier<< #carrier
end
ROLES = %w[admin moderator subscriber]
#Each user can subscribe to many channels
has_and_belongs_to_many :channels
#Each user who is a moderator can moderate many channels
#has_many :channel_mods
has_and_belongs_to_many :modifies , :class_name => "Channel"
#Each user can receive many messages
has_and_belongs_to_many :messages
#Each user belongs to a carrier
belongs_to :carrier
#Filter users by role(s)
named_scope :with_role, lambda { |role| {:conditions => "roles_mask & #{2**ROLES.index(role.to_s)} > 0 "} }
def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end
def role_symbols
roles.map do |role|
role.underscore.to_sym # NOT role.name.underscore.to_sym (role is a string)
end
end
end
carrier.rb
class Carrier < ActiveRecord::Base
has_many :users
named_scope :with_name, lambda {|name| {:conditions => {:name => name}}}
end
user controller
class UsersController < ApplicationController
filter_resource_access
# GET /users
# GET /users.xml
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
end
end
# GET /users/1
# GET /users/1.xml
def show
##user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
# GET /users/new
# GET /users/new.xml
def new
##user = User.new
#carriers = Carrier.find(:all)
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #user }
end
end
# GET /users/1/edit
def edit
##user = User.find(params[:id])
end
def create
##user = User.new(params[:user])
##user.channels << Channel.find(1)
respond_to do |format|
if #user.save
format.html { redirect_to(:channels, :notice => 'Registration successfully.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
def profile
#user = User.find(params[:id])
end
# PUT /users/1
# PUT /users/1.xml
def update
##user = current_user
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(#user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.xml
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.xml { head :ok }
end
end
def delete
#user = User.find(params[:user_id])
#user.destroy
redirect_to :users
end
end
You might be using an array instead of a singular item. belongs_to relationships are either nil or a model.
You may have intended to do a direct assignment:
def set_carrier
self.carrier = Carrier.with_name(self.carrier_name).first
end
The << operator is used to append items to arrays, but carrier is not an array. This may trip up something deeper in Rails.
Just in case this helps anyone: I was getting this error message too with a model like this:
def Foo
has_many :bars
has_many :thingies, through: :bars
end
I was then doing this in the controller;
Foo.all.includes(:bars, :thingies)
and it blew up trying to go through scopes. The fix was to remove "bars" from the includes call - they will be automatically eager-loaded because "thingies" is being eager loaded.