Polymorpic paperclip working strange with Rails 5 - ruby-on-rails

view:
<div class="kitchen_settings">
<%= form_for #kitchen, :html => { :multipart => true, "data-ajax" => false} do |f| %>
<%= f.text_field :title,placeholder: "#{current_user.fullname}'s Kitchen", autofocus: true %>
<%= f.text_field :bio, placeholder: 'something about your kitchen' %>
<%= f.fields_for :picture do |photo| %>
<%= photo.file_field :avatar %>
<% end %>
<%= f.submit %>
<% end %>
kitchen_controller.rb
class KitchensController < ApplicationController
before_action :authenticate_user!
def new
#kitchen = Kitchen.new
#kitchen.build_picture
end
def create
#kitchen = current_user.build_kitchen(kitchen_params)
respond_to do |format|
if #kitchen.save
format.html { redirect_to dashboard_path, notice: 'Kitchen was successfully created.' }
format.json { render :show, status: :created, location: dashboard_path }
else
format.html { render :new }
format.json { render json: #kitchen.errors, status: :unprocessable_entity }
end
end
end
def show
#kitchen = Kitchen.find (params[:id])
end
private
def kitchen_params
params.require(:kitchen).permit(:title,:bio, picture_attributes: [:avatar])
end
end
kitchen.rb
class Kitchen < ApplicationRecord
belongs_to :user
has_one :picture, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :picture
end
picture.rb
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100"}, default_url: "/assets/:style/missing.png"
validates_attachment :avatar, :presence => true,
:content_type => { :content_type => ["image/jpeg", "image/gif", "image/png"] },
:size => { :in => 0..500.kilobytes }
end
And its giving me this error
I wanted polymorphic picture model. I decided to deal with polymorphic picture association but its just rollbacks always... Stuck. I've attached the image from the console. Thanks!!
Debugged it using binding.pry

In Rails 5, whenever we define a belongs_to association, it is required to have the associated record present by default after this change.To change this behavior i managed to do it this way:
picture.rb
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true, optional: true
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100"}, default_url: "/assets/:style/missing.png"
validates_attachment :avatar, :presence => true,
:content_type => { :content_type => ["image/jpeg", "image/gif", "image/png"] },
:size => { :in => 0..500.kilobytes }
end

There is a problem with you validation of imageable in image.rb
When you store kitchen alongwith image in single go. Imageable validation is failed for image as kitchen is not created yet. That's why it's rollback.
To confirm it you can remove the validation temporarily.
You need to use inverse_of to make this work
in image.rb
belongs_to :imageable, polymorphic: true , inverse_of: :image
in kitchen.rb
has_one :picture, as: :imageable, dependent: :destroy,inverse_of: :imageable
I added inverse_of in both the associations. It tells rails that these associations are inverse to each other so if any of it's set then don't make a query to fetch the other.
In this way if you set any association then other will be set automatically and validation won't fail.
Here is a nice blog for inverse_of usage.

Related

undefined method `image' for Album

I am currently trying to display photos in an album.
This is the error that I receive (image below):
These are the current specs so far:
Album Model
class Album < ApplicationRecord
has_many :photos, dependent: :destroy
validates :title, presence: true
validates :description, presence: true
end
Photo Model
class Photo < ApplicationRecord
belongs_to :album
validates :name, presence: true
validates :image, attachment_presence: true
has_attached_file :image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
end
Photo Controller
class PhotosController < ApplicationController
def create
#album = Album.find(params[:album_id])
#photo = #album.photos.create(photo_params)
redirect_to album_path(#album)
end
def destroy
#album = Album.find(params[:album_id])
#photo = #album.photos(params[:id])
#photo.destroy
redirect_to album_path(#album)
end
private
def photo_params
params.require(:photo).permit(:name,:image)
end
end
View Folder - Photos/_photos.html.erb - where the error is generated from
<p>
<strong>Name:</strong>
<%= photo.name %>
</p>
<p>
<%= image_tag #album.image.url(:medium) %>
</p>
How do I fix this error?
Please let me know if more information is needed.
Thank you.
Instead of this line
<%= image_tag #album.image.url(:medium) %>
try this
<%= image_tag photo.image.url(:medium) %>

Nested models with simple_form and paperclip

[This maybe a simple issue, but I just can't find a solution]
I'm trying to update an existing record with new data and add some image(s) to that record (via paperclip), however, my code doesn't show the paperclip field at all.
I've followed this example and also tried with this solutions, but nothing works.
I have two, models Hotel and HotelImage.
hotel.rb
class Hotel < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
validates :name, presence: true
validates :location, presence: true
has_many :hotel_images
accepts_nested_attributes_for :hotel_images
def slug_candidates
[
[:name, :location],
]
end
end
hotel_image.rb
class HotelImage < ActiveRecord::Base
belongs_to :hotel
#rails generate paperclip hotel_image image
has_attached_file :image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
edit.html.erb
<%= simple_form_for #hotel, :url => admin_hotel_path, method: :put, html: { multipart: true } do |f| %>
<%= f.error_notification %>
<%= f.input :name %>
<%= f.input :location %>
<%= f.simple_fields_for :hotel_images do |ff| %>
<%= ff.input :image, as: :file %>
<% end %>
<%= f.button :submit %>
<% end %>
hotels_controller.rb
#other code
def update
#hotel = Hotel.friendly.find(params[:slug])
if #hotel.update(hotel_params)
redirect_to admin_hotels_path, notice: 'Hotel was successfully updated.'
else
render :edit
end
end
private
def hotel_params
params.require(:hotel).permit(:name, :location, :admin_id, hotel_images_attributes: [:hotel_id, :image])
end
Here's the print screen of the form, where you can see that the paperclip field is missing:
However, when I change f.simple_fields_for :hotel_images to, for example, f.simple_fields_for :foobars, the paperclip field is shown:
Note: the name and location attributes update just fine.
def update
#hotel = Hotel.friendly.find(params[:slug])
if #hotel.update(hotel_params)
redirect_to admin_hotels_path, notice: 'Hotel was successfully updated.'
else
render :edit
end
end
def edit
#hotel_images = #hotel.hotel_images.present? ? #hotel.hotel_images : #hotel.hotel_images.build
end
Try this code

Rails nested form: update creates multiple records

I have menu model and photos model where the menu has has_many relationship with photo. For the image upload, I'm using paperclip. I was able to build a nested_form that creates photo and other attributes in photos table. However, when I update, the record gets duplicated in photos table and the new photo selected in the update form will not be uploaded. Appreciate your help.
menu model
class Menu < ActiveRecord::Base
has_many :photos, :dependent => :destroy
accepts_nested_attributes_for :photos, reject_if: :all_blank, allow_destroy: true
end
photo model
class Photo < ActiveRecord::Base
belongs_to :menu
has_attached_file :image,
:styles => { :thumb => "100x100#", :medium => "300x300#", :large => "600x400>" },
:url => "/assets/menus/photos/images/:id/:style/:basename.:extension",
:path => "#{Rails.root}/public/assets/menus/photos/images/:id/:style/:basename.:extension"
validates_attachment :image, content_type: { content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"] }
end
form.html.haml
= simple_form_for #menu do |f|
= f.simple_fields_for :photos do |photo|
= render 'photo_fields', f: photo
_photo_field.html.haml
.nested-fields
= f.file_field :image
= f.input :main_flag, as: :hidden, input_html: { value: "true" }
= f.input :user_id, as: :hidden, input_html: { value: "1"}
menus_controller.rb
class MenusController < ApplicationController
...
def update
#menu = Menu.find(params[:id])
if #menu.update(menu_params)
if params[:image]
#menu.photos.destroy
#menu.photos.build(menu_params)
end
flash[:success]= 'Menu was successfully updated'
redirect_to brand_menus_path(#menu.brand_id)
else
render 'index'
end
end
private
def menu_params
params.require(:menu).permit(:name, :price, :brand_id, :category_id, :description,
photos_attributes: [:user_id, :image, :main_flag])
end
This is very common problem with the strong_parameters when using with nested_params. Whitelisting the :id in the photos_attributes should fix your problem
def menu_params
params.require(:menu).permit(:name, :price, :brand_id, :category_id, :description,
photos_attributes: [ :id, :user_id, :image, :main_flag])
end

Paperclip will delete current image even though it's not changed on update

I'm using Paperclip to let my users upload images.
It works pretty well with separated single resources. I can add, edit, and delete all images without any extra effort using a RESTFUL structure.
But I have some difficulties while working with nested attributes.
I have 2 models called Article and Headline.
I'm using an Article form in my view to create an associated instance of Headline. So I'm using accepts_nested_attributes_for with ActiveRecord. I've set up everything related to this. I'm using fields_for in my form view. Strong parameters settings are all done. Everything works great with adding a NEW article with its headline. I also have a title field which updates appropriately.
<%= f.fields_for :headline do |r| %>
<%= r.label :title %>
<%= r.text_field :title %>
<%= r.label :image, "Headline image" %>
<%= r.file_field :image %>
<% end %>
But when I UPDATE an article without any change of the Headline's image, the Headline's image disappears.
As I know and experience, Paperclip doesn't update image files unless they are changed. That's the expected behavior. But in this case, it overwrites the nested model's attribute even though it shouldn't.
I think there must be a workaround for this?
Article.rb:
class Article < ActiveRecord::Base
after_commit :expire_caches
scope :desc, ->{ order(created_at: :desc)}
scope :with_images, -> {where.not(image_file_name: nil)}
before_validation :set_default_category
acts_as_taggable
attr_accessor :is_headline
has_attached_file :image, default_url: "default_:style.png",
:styles => { :large=>"700", :medium => "296", :thumb => "150", mini_thumb: "50x50>", :absolute_thumb=>"150x150#" },
:s3_host_name => 's3-eu-west-1.amazonaws.com',
:path => "articles_images/:id/:style/:basename.:extension"
#validates :title, presence: true
belongs_to :category
belongs_to :user
validates :title, :content, presence: true
validates :content, length: {minimum: 10}
validates :category_id, presence: true
validates :cihan_url, uniqueness: true, allow_nil: true
has_many :galleries, dependent: :destroy
has_one :headline, dependent: :destroy
has_one :ad, dependent: :destroy
has_many :videos
accepts_nested_attributes_for :headline
def self.main_page
where(main_page: true).with_images.desc
end
def to_param
"#{self.id}-#{self.title.parameterize}"
end
private
def set_default_category
self.category = Category.uncategorized if self.category.blank?
end
def expire_caches
Rails.cache.clear
end
end
Headline.rb:
class Headline < ActiveRecord::Base
after_commit :expire_caches
has_attached_file :image, default_url: "default_:style.png",
:styles => { :large=>"600", :medium => "300x300>", :thumb => "150x150>", :absolute_thumb=>"150x150#" },
:s3_host_name => 's3-eu-west-1.amazonaws.com',
:path => "headline_images/:id/:style/:basename.:extension"
belongs_to :article
private
def expire_caches
Rails.cache.clear
end
end
From articles_controller:
def set_article
#article = Article.find(params[:id])
end
def update
if #article.update(article_params)
redirect_to [:admin, #article], flash: {success: "<i class=\"icon-ok\"></i> Haber başarıyla düzenlendi.".html_safe}
else
render action: 'edit'
end
end
def article_params
params.require(:article).permit(:title, :content, :publish_date, :category_id, :active, :tag_list, :main_page, :extra_title, :image, :is_headline, headline_attributes: [:title, :counter, :order, :image])
end

rails 3 polymorphic association with paperclip and multiple models

I want to make polymorphic associations with paperclip, and allow my user to have one avatar and multiple images.
Attachment model:
class Attachment < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
end
class Avatar < Attachment
has_attached_file :image, :styles => { :thumb => "150x150>", :view => "260x180>" },
end
class Image < Attachment
has_attached_file :image, :styles => { :thumb => "150x150>", :view => "260x180>" },
end
User Model:
has_one :avatar, :as => :attachable, :class_name => 'Attachment', :conditions => {:type => 'avatar'}
accepts_nested_attributes_for :avatar
User Controller:
def edit
#user.build_avatar
end
User View form:
<%= form_for #user, :html => { :multipart => true } do |f| %>
<%= f.fields_for :avatar do |asset| %>
<% if asset.object.new_record? %>
<%= asset.file_field :image %>
<% end %>
<% end %>
when I attempt to save the changes I get the error => unknown attribute: avatar
if I remove the :class_name => 'attachment' in the has_one association I get the error =>
uninitialized constant User::Avatar
I need to also attach avatars to blog posts, so I need the association to be polymorphic (or atleast i think so)
I am stumped and any help would be greatly appreciated.
I do have a project in the works that is successfully using Paperclip and polymorphic associations. Let me show you what I have, and maybe you can apply it to your project:
class Song < ActiveRecord::Base
...
has_one :artwork, :as => :artable, :dependent => :destroy
accepts_nested_attributes_for :artwork
...
end
class Album < ActiveRecord::Base
...
has_one :artwork, :as => :artable, :dependent => :destroy
accepts_nested_attributes_for :artwork
...
end
class Artwork < ActiveRecord::Base
belongs_to :artable, :polymorphic => true
attr_accessible :artwork_content_type, :artwork_file_name, :artwork_file_size, :artwork
# Paperclip
has_attached_file :artwork,
:styles => {
:small => "100",
:full => "400"
}
validates_attachment_content_type :artwork, :content_type => 'image/jpeg'
end
the songs form and the albums form include this as a partial:
<div class="field">
<%= f.fields_for :artwork do |artwork_fields| %>
<%= artwork_fields.label :artwork %><br />
<%= artwork_fields.file_field :artwork %>
<% end %>
don't forget to include :html => { :multipart => true } with the form
artworks_controller.rb
class ArtworksController < ApplicationController
def create
#artwork = Artwork.new(params[:artwork])
if #artwork.save
redirect_to #artwork.artable, notice: 'Artwork was successfully created.'
else
redirect_to #artwork.artable, notice: 'An error ocurred.'
end
end
end
and finally, an excerpt from songs_controller.rb:
def new
#song = Song.new
#song.build_artwork
end
I'm not sure you really need to be polymorphic. How about this approach, which uses has_many :through? In plain English, the user has one avatar which has multiple images, and through this association you can call User.images to get the collection of images associated with the avatar.
http://guides.rubyonrails.org/association_basics.html
class User < ActiveRecord::Base
has_one :avatar
has_many :images, :through => :avatar
end
class Avatar < ActiveRecord::Base
belongs_to :user
has_many :images
end
class Image < ActiveRecord::Base
belongs_to :avatar
has_attached_file :image, :styles => { :thumb => "150x150>", :view => "260x180>" },
end
Having said all of this, I am left to wonder why you need to go through all this anyway. Why not just do
class User < ActiveRecord::Base
has_many :avatars
end
which would give you as many images (avatars) as you want.

Resources