I am working on a rails app and making a nested resource for pictures. The model is Picture, and it is polymorphic and interacts with a couple different tables.
I am working on the controller action, and the create method. According to this answer and other things I have seen with paperclip, the params should follow the format of table_name and then file. Even when doing that, I am still getting a param is missing or the value is empty: picture error.
Here is my code:
Picture.rb
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
has_attached_file :image, style: { small: '64x64', medium: '100x100', large: '200x200' }
validates_attachment :image, presence: true, content_type: { content_type: /\Aimage\/.*\Z/ },
size: { in: 0..5.megabytes }, default_url: 'missing_img.png'
acts_as_list scope: [:imageable_id, :imageable_type]
end
pictures_controller.rb
class PicturesController < ApplicationController
before_action :authorize_user!
before_action :set_resource!
def index
#pictures = #resource.pictures
end
def create
#picture = #resource.pictures.new(picture_params) do |pic|
pic.imageable_type = #resource
pic.imageable_id = #resource.id
end
if #picture.save
redirect_to :back
flash[:success] = 'Image Saved!'
else
redirect_to :back
flash[:danger] = "#{#picture.errors.full_messages.to_sentence}"
end
end
def destroy
#picture = Picture.find(params[:id])
#resource.pictures.delete(#picture)
redirect_to :back
flash[:success] = "Picture deleted"
end
private
def set_resource!
klass = [Gym, User, Location, Product].detect { |c| params["#{c.name.underscore}_id"] }
#resource = klass.find(params["#{klass.name.underscore}_id"])
end
def picture_params
params.require(:picture).permit(:image)
end
end
pictures/index.html
<h6>Upload Pictures</h6>
<%= form_for(current_user, url: url_for(controller: 'pictures', action: 'create'), method: :post, html: { multipart: true, class: 'form-horizontal' }) do |f| %>
<%= f.file_field :image, type: :file, multiple: true, style: 'padding-bottom: 25px;' %>
<%= f.submit "Upload", class: "btn btn-gen" %>
<% end %>
And here is the param request on form submit:
{"utf8"=>"✓",
"authenticity_token"=>"+fsA6liECF7pkUp/0BA0wDHq9Vv63jB+WBb7O/uUEDhhmIOZ22Rb1rNWDwuwPTDPNS7jg7vP/fVCVllDV21wDw==",
"user"=>{"image"=>[#<ActionDispatch::Http::UploadedFile:0x007f87b138acf8 #tempfile=#<Tempfile:/var/folders/9c/1_0mk00n297f560_fpv9jzl40000gn/T/RackMultipart20161020-25582-3qt3gc.jpg>,
#original_filename="b4lROOR.jpg",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"user[image][]\"; filename=\"b4lROOR.jpg\"\r\nContent-Type: image/jpeg\r\n">]},
"commit"=>"Upload",
"user_id"=>"15"}
Even with this, I am still getting the error. Does anyone see anything wrong I'm doing in my code that I could fix? Any help is much appreciated.
The problem is that your picture_params method requires the route param key picture while your form has the key user.
Lets do some refactoring:
private
def set_resource!
#resource = resource_class.find(params[param_key + "_id"])
end
def resource_class
#resource_class ||= [Gym, User, Location, Product].detect do |c|
params[ param_key(c) + "_id" ]
end
end
def param_key(klass = resource_class)
ActiveModel::Naming.param_key(klass)
end
def picture_params
params.require(param_key).permit(:image)
end
Instead of klass.name.underscore we use ActiveModel::Naming which is how rails figures out how to use models for stuff like routes, params or translations.
I figured out a temporary work around. Definitely not ideal, and if anyone has a better suggestion I would love to hear it. But here is what I currently have that does work.
def create
pic = nil
if params[:user]['image']
params[:user]['image'].each do |image|
pic = #resource.pictures.create(image: image, imageable_type: #resource, imageable_id: #resource.id)
end
if pic.save
redirect_to :back
flash[:success] = 'Image Saved!'
else
redirect_to :back
flash[:danger] = "#{pic.errors.full_messages.to_sentence}"
end
else
flash[:danger] = "Picture file not found"
redirect_to :back
end
end
Related
Every time I go to this link /books/2/chapter I get this error:
ChaptersController#index is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: []
Please show me where I am going wrong and any other improvements I can make to my code.
These are my controllers
class ChaptersController < ApplicationController
def show
#chapter =Chapter.find(params[:id])
#sections = Section.all
end
def index
#chapters = Chapter.all
#book = Book.find(params[:book_id])
end
def new
#chapter = Chapter.new
#book = Book.find(params[:book_id])
end
def edit
#chapter = Chapter.find(params[:id])
end
def create
#chapter = Chapter.new(chapter_params)
#book = Book.find(params[:book_id])
if #chapter.save
redirect_to #chapter
else
render 'new'
end
end
def update
#chapter = Chapter.find(params[:id])
if #chapter.update(chapter_params)
redirect_to #chapter
else
render 'edit'
end
end
def destroy
#chapter = Chapter.find(params[:id])
#chapter.destroy
redirect_to chapters_path
end
private
def chapter_params
params.require(:chapter).permit(:title,:text)
end
end
and
class BooksController < ApplicationController
def show
#book = Book.find(params[:id])
#chapters = Chapter.all
end
def index
#books = Book.all
end
def new
#book = Book.new
end
def edit
#book = Book.find(params[:id])
end
def create
#book = Book.new(book_params)
if #book.save
redirect_to #book
else
render 'new'
end
end
def update
#book = Book.find(params[:id])
if #book.update(book_params)
redirect_to #book
else
render 'edit'
end
end
def destroy
#book = Book.find(params[:id])
#book.destroy
redirect_to books_path
end
private
def book_params
params.require(:book).permit(:title,:text,:bookcover,:authorpic,:author)
end
end
These are my models
class Chapter < ApplicationRecord
has_many :sections, dependent: :destroy
belongs_to :book
validates :title, presence: true,
length:{minimum: 5}
end
and
class Book < ApplicationRecord
has_many :chapters, dependent: :destroy
has_attached_file :bookcover, styles: { medium: "300x300>", thumb: "100x100>" }
has_attached_file :authorpic, styles: { medium: "300x300>", thumb: "100x100>" }
validates_attachment_content_type :bookcover, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates_attachment_content_type :authorpic, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates :title, presence: true,
length:{minimum: 5}
end
These are my routes
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
get 'about', to: 'pages#about'
resources :contacts, only: :create
get 'contact-us', to: 'contacts#new', as: 'new_contact'
get 'bookclub', to: 'pages#bookclub'
get 'podcast', to: 'pages#podcast'
resources :essays do
resources :comments
end
resources :podcasts do
resources :podcomments
end
resources :books do
resources :chapters do
resources :sections do
resources :bookcomments
end
end
end
end
This is my chapters/_index.html.erb file
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col"><h3>Chapters</h3>
</div>
</div>
<%= link_to 'New chapter', new_book_chapter_path(#book) %>
<% #chapters.each do |chapter| %>
<ul class="demo-list-item mdl-list">
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<%=link_to chapter.title, book_chapter_path(#book, chapter)%>
</span>
</li>
</ul>
<%= link_to 'Edit', edit_book_chapter_path(#book, chapter) %>
<%= link_to 'Destroy', book_chapter_path(#book, chapter),
method: :delete,
data: { confirm: 'Are you sure?' } %>
<%end%>
This is my books/show.html.erb file
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col"><h3><%= #book.title %></h3>
</div>
<%= render 'chapters/index', chapters: #chapters %>
<p>
<%= link_to 'Edit', edit_book_path(#book) %> |
<%= link_to 'Back', books_path %>
</p>
Why chapters/_index and not chapters/index? This must be the error.
I think you have some errors in your controllers.
def index
# Do you really want all chapters? (from all books)
# #chapters = Chapter.all
#book = Book.find(params[:book_id])
# I think you want only this book's chapters
#chapters = #book.chapters
end
def show
#chapter =Chapter.find(params[:id])
# The same thing. You want only sections from this chapter
# #sections = Section.all
#sections = #chapter.sections
end
EDIT
I see you are using chapters/_index as a partial from book/show. But you are also using the same in ChaptersController#index. Although not very nice you could do this:
chapters/index.html.erb
<%= render partial: 'chapters/_index', locals: { book: #book, chapters: #chapters } %>
in chapters/_index, replace #chapters by chapters (without #) and #book by book (without #)
When I try to create new record image doesn't upload but when I try to edit everything working fine. No errors, after create I have nil in DB after Update I have a link to a picture and everything fine. What I did wrong? Before I used form_for and everything was working fine, then I change form_for to simple_form. Maybe problem here?
Controller:
def create
#byebug
#bar = current_user.bars.new(bar_params)
if #bar.save
UserMailer.admin_verify_email(#bar.id).deliver_now
flash[:success] = t(".bar_created_successfully")
# if admin_verified add bar to the search bos
if #bar.admin_verified
Barsearchsuggestion.index_bar(#bar)
redirect_to bars_path(my_bar: true)
else
redirect_to bar_status_user_path(current_user)
end
else
flash.now[:alert] = #bar.errors.full_messages
render 'users/add_bar'
end
end
def edit
#bar = Bar.find(params[:id])
end
def update
#bar = Bar.find(params[:id])
if #bar.update_attributes(bar_params)
flash[:success] = "Bar updated."
redirect_to bar_status_user_path(current_user)
else
flash.now[:error] = I18n.t("simple_form.error_notification.default_message")
# flash[:error] = #bar.errors.to_array
render 'edit'
end
end
private
def bar_params
params.require(:bar).permit!
end
View:
=simple_form_for(#bar) do |f|
.form-group
= f.label :profile_picture
= t(".profile_picture_ext_html")
%br
= image_tag f.object.profile_picture.bar_detail.url
= f.file_field :profile_picture, class: 'form-control'
Model:
mount_uploader :profile_picture, BarPictureUploader
You have to permit the profile_picture parameter like this:
def bar_params
params.require(:bar).permit(:profile_picture)
end
Try setting multipart: true in your form
I'm having some trouble with Nokogiri.
I'm new to this xml parsing and I seem to get no errors. I get the order imported alert.
Which means it may be permitted perimeters. If I'm right then, Could I create a model that allows all permitted perimeters. So please take a look, thank you for your time, here is my code:
Routes.rb
resources :orders do
collection { post :import }
get "/confirm" => "confirmations#show"
get 'dconfirmation' => 'orders#confirmation'
end
Model
class Order < ActiveRecord::Base
belongs_to :user
scope :not_completed_orders, -> {
where(:complete => false)
}
require 'nokogiri'
def self.import(file)
doc = Nokogiri::XML.parse(file)
#your processing code goes here
end
end
Controller Order
class OrdersController < ApplicationController
before_filter :authenticate_user!
def new
#order = Order.new
end
def create
#order = current_user.orders.new(order_params)
#order.email = current_user.email
#order.name = current_user.name
#order.address_line_1 = current_user.address_line_1
#order.address_line_2 = current_user.address_line_2
#order.postcode = current_user.postcode
#order.city = current_user.city
#order.country = current_user.country
if #order.save
redirect_to dconfirmation_path
end
end
def order_params
params.require(:order).
permit(
:email,
:delivery_name,
:company_name,
:delivery_address1,
:delivery_address2,
:delivery_address3,
:delivery_city,
:delivery_postcode,
:delivery_country,
:phone,
:package_contents,
:description_content,
:restricted_items,
:terms_conditions,
:insurance,
:contents_value,
:cf_reference,
:reference_number,
:service
)
end
def show
#user = User.find(params[:id])
end
def confirmation
end
def import
Order.import(params[:file])
redirect_to root_url, notice: "Order imported."
end
end
View
= form_tag import_orders_path, multipart: true do
= file_field_tag :file
= submit_tag "Import"
I trying write params to Company model. But I have error undefined method `model_name' for NilClass:Class in this point = simple_form_for #company, url: update_settings_company_path do |f|. Where I must set #company?
Controller
def change_settings
#vacation_days = current_company.vacation_days
#illnes_days = current_company.illnes_days
end
def update_settings
if #company.update(company_params)
redirect_to account_company_path, notice: t('company.settings_changed')
else
render action: 'change_settings'
end
end
private
def company_params
params.require(:company).permit(:vacation_days, :illnes_days)
end
View
.company_settings
= simple_form_for #company, url: update_settings_company_path do |f|
= f.error_notification
= f.input :vacation_days
= f.input :illnes_days
%br
= f.submit t('common.save'), class: 'btn'
= link_to t('common.back'), account_company_path, class: 'btn'
routes
resource :company, only: :all do
get :change_settings
post :update_settings
patch :update_settings
end
What's wrong? Help me please
You don't set #company instance variable in both your actions. You can do it using before_filter, like this:
before_filter :find_company
def change_settings
#vacation_days = current_company.vacation_days
#illnes_days = current_company.illnes_days
end
def update_settings
if #company.update(company_params)
redirect_to account_company_path, notice: t('company.settings_changed')
else
render action: 'change_settings'
end
end
private
def company_params
params.require(:company).permit(:vacation_days, :illnes_days)
end
def find_company
#company = current_company
end
Try this instead, You need to set the instance variable before you use it. By default the instance variable is set to nil.
def update_settings
#company = current_company
if #company.update(company_params)
redirect_to account_company_path, notice: t('company.settings_changed')
else
render action: 'change_settings'
end
end
My goal is to enable a user to be able to submit multiple NewsImages from a parent Blog form.
My Blog model looks like this:
# == Schema Information
# Schema version: 20091006171847
#
# Table name: blogs
#
# id :integer(4) not null, primary key
# title :string(255)
# body :text
# profile_id :integer(4)
# created_at :datetime
# updated_at :datetime
#
class Blog < ActiveRecord::Base
has_many :comments, :as => :commentable, :order => "created_at asc"
has_many :news_images, :dependent => :destroy
belongs_to :profile
accepts_nested_attributes_for :news_images
validates_presence_of :title, :body
attr_immutable :id, :profile_id
def after_create
feed_item = FeedItem.create(:item => self)
([profile] + profile.friends + profile.followers).each{ |p| p.feed_items << feed_item }
end
def to_param
"#{self.id}-#{title.to_safe_uri}"
end
end
My NewsImage model looks like this:
class NewsImage < ActiveRecord::Base
belongs_to :blog
validates_each :blog do |news_image, attr, value|
news_item.errors.add attr, "You are only limited to 5 images." if news_item.blog.news_items.size >= 5
end
has_attached_file :image, :styles => { :original => "1920x1080>", :square => "158x158#", :thumb => "386x155#", :slide_thumb => "165x67#"},
:url => "/system/:attachment/:id/:style/:basename.:extension",
:path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension"
def validate
dimensions = Paperclip::Geometry.from_file(self.image.queued_for_write[:original])
self.errors.add(:image, "Please upload an image that is at least 476 pixels wide") if dimensions.width < 476
self.errors.add(:image, "Please upload an image that is at least 319 pixels high") if dimensions.height < 319
end
end
My Blog controller looks like this:
class BlogsController < ApplicationController
skip_filter :login_required, :only => [:index, :show]
prepend_before_filter :get_profile
before_filter :setup
def index
if #p && #p == #profile && #p.blogs.empty?
flash[:notice] = 'You have not create any blog posts. Try creating one now.'
redirect_to new_profile_blog_path(#p) and return
end
respond_to do |wants|
wants.html {render}
wants.rss {render :layout=>false}
end
end
def create
#blog = #p.blogs.build params[:blog]
respond_to do |wants|
if #blog.save
wants.html do
flash[:notice] = 'New blog post created.'
redirect_to profile_blogs_path(#p)
end
else
wants.html do
flash.now[:error] = 'Failed to create a new blog post.'
render :action => :new
end
end
end
end
def show
render
end
def edit
render
end
def update
respond_to do |wants|
if #blog.update_attributes(params[:blog])
wants.html do
flash[:notice]='Blog post updated.'
redirect_to profile_blogs_path(#p)
end
else
wants.html do
flash.now[:error]='Failed to update the blog post.'
render :action => :edit
end
end
end
end
def destroy
#blog.destroy
respond_to do |wants|
wants.html do
flash[:notice]='Blog post deleted.'
redirect_to profile_blogs_path(#p)
end
end
end
protected
def get_profile
#profile = Profile[params[:profile_id]]
end
def setup
#user = #profile.user
#blogs = #profile.blogs.paginate(:page => #page, :per_page => #per_page)
if params[:id]
#blog = Blog[params[:id]]
else
#blog = Blog.new
3.times {#blog.news_images.build }
end
end
def allow_to
super :owner, :all => true
super :all, :only => [:index, :show]
end
end
My form currently is this:
<%
#locals
blog ||= #blog
%>
<div id="blog">
<% less_form_for [#profile, blog], :html => {:multipart => true} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<% f.fields_for :news_images do |builder| %>
<%= builder.label :caption %>
<%= builder.text_field :caption %>
<%= builder.label :image %>
<%= builder.file_field :image %>
<% end %>
To include a youtube video use: [youtube: address_of_video]
<div class="row button">
<%= f.submit 'Save', :class=>'button' %>
</div>
<% end %>
</div>
I keep getting the following error:
ActionView::TemplateError (`#blog[news_images_attributes][0]' is not allowed as an instance variable name) on line #12 of app/views/blogs/_form.html.erb:
If I remove:
<%= builder.text_field :caption %>
the error goes away. In fact if i change the text field to a file field the error goes away.
I'm really confused here as I'm not sure why one form helper works fine and the other doesn't.
Thank you very much for looking =)
Update:
This is my less_form_builder.rb file:
class LessFormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Helpers::ActiveRecordHelper
def method_missing *args
options = args.extract_options!
label = get_label '', options
front(label) + super(*args) + back(label)
end
def wrap method, options = {}
s = front(method, options)
s += yield if block_given?
s += back(method, options)
end
# Generates a label
#
# If +options+ includes :for,
# that is used as the :for of the label. Otherwise,
# "#{this form's object name}_#{method}" is used.
#
# If +options+ includes :label,
# that value is used for the text of the label. Otherwise,
# "#{method titleized}: " is used.
def label method, options = {}
text = options.delete(:label) || "#{method.to_s.titleize}: "
if options[:for]
"<label for='#{options.delete(:for)}'>#{text}</label>"
else
#need to use InstanceTag to build the correct ID for :for
ActionView::Helpers::InstanceTag.new(#object_name, method, self, #object).to_label_tag(text, options)
end
end
def select method, options = {}
front(method, options) + super + back(method, options)
end
def text_field method, options = {}
front(method, options) + super + back(method, options)
end
def password_field method, options = {}
front(method, options) + super + back(method, options)
end
def text_area method, options = {}
front(method, options) + super + back(method, options)
end
def check_box method, options = {}
front(method, options) + super + back(method, options)
end
def calendar_field method, options = {}
expired = options.delete(:expired) || false
if not expired; options.merge!(:class => 'calendar'); else; options.merge!(:disabled => true); end
text_field method, options
end
def front method = '', options = {}
"<div class='row clear'>#{label(method, options)}"
end
def back method = '', options = {}
"#{error_messages_on( object_name, method ) unless method.blank?}
<div class='clear'></div>
</div>"
end
end