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.
Related
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.
I use paperclip to upload multi-file attached to studentcourseassignment,but i fail.
model
class StudentCourseAssignment < ActiveRecord::Base
attr_accessible :score, :comment, :finish_status,:attachments
accepts_nested_attributes_for :attachments
belongs_to :assignment
belongs_to :user
has_many :attachments ,:as => :attachmentable,:dependent => :destroy
end
class Attachment < ActiveRecord::Base
attr_accessible :user_upload
belongs_to :attachmentable , :polymorphic => true
has_attached_file :user_upload
end
controller
**new**
#sca = StudentCourseAssignment.new
#sca.attachments.build
#sca.attachments.build
**create**
#sca = StudentCourseAssignment.new(params[:student_course_assignment])
#assignment = Assignment.find(params[:assignment_id])
#sca.user = current_user
#sca.assignment = #assignment
if #sca.save
flash[:alert] = "success"
redirect_to course_years_path
else
flash[:alert] = "fail"
redirect_to course_years_path
end
** view**
<%= form_for #sca, :url => assignment_student_course_assignments_path(#assignment),
:html => { :id => 'student-assignment-form', :multipart => true } do |f| %>
file:
<%= f.fields_for :attachments do |a_f| %>
<%= a_f.file_field :user_upload %>
<%= submit_tag "create" %>
<% end%>
<% end %>
wrong
No association found for name `attachments'. Has it been defined yet?
if remove accepts_nested_attributes_for :attachments,it's still wrong
Attachment(#70201401779680) expected, got Array(#70201383294620)
hope your help!thx!
Change
from:
attr_accessible :score, :comment, :finish_status,:attachments
to:
attr_accessible :score, :comment, :finish_status,:attachments_attributes
I realize this is an old question, but fwiw I think you'll need to move
accepts_nested_attributes_for :attachments
to appear after
has_many :attachments, :as => :attachmentable,:dependent => :destroy
I hit this in a project once myself; pretty sure it boils down to accepts_nested_attributes_for expecting the relation to already be declared before its invoked.
In my first Ruby on Rails app I've a one-to-many association
class Battle < ActiveRecord::Base
has_many :rivals, :dependent => :destroy
accepts_nested_attributes_for :rivals, :allow_destroy => true
attr_accessible :question, :rivals_attributes
end
class Rival < ActiveRecord::Base
belongs_to :battle
has_attached_file :rival_image, :styles => { :normal => "300x300>", :thumb => "100x100>" }
end
Let's say a battle has 2 rivals
<% for rival in #battle.rivals %> <%= rival.name %> <% end %> displays both rivals that belongs to battle
How do I display first rival and the second one where I need it?
If you want to access individual records from a association use:
<%= battle.rivals[0].name %>
3 models
class Country < ActiveRecord::Base
has_many :regions
has_many :assets, :dependent => :destroy
accepts_nested_attributes_for :assets
end
class Region < ActiveRecord::Base
belongs_to :country
has_many :appartments
has_many :assets, :dependent => :destroy
accepts_nested_attributes_for :assets
end
class Asset < ActiveRecord::Base
belongs_to :region
belongs_to :country
has_attached_file :image,
:styles => {
:thumb=> "100x100>",
:small => "300x300>",
:large => "600x600>"
}
end
The edit form
<%= f.fields_for :assets do |asset| %>
<% if asset.object.new_record? %>
<%= asset.file_field :image %>
<% end %>
<% end %>
My region controller:
def edit
#country = Country.find(params[:country_id])
# For URL like /countries/1/regions/2/edit
#region = #country.regions.find(params[:id])
5.times { #country.region.assets.build }
end
I get
"undefined method `region' for #<Country:0x007fbf8b848198>"
Someone ideas?
5.times { #country.region.assets.build }
should be
5.times { #region.assets.build }
I'm trying to delete each single image uploaded for a model (work has many images) but now my code works like this: I have three images uploaded in a work, I want to delete just one but when I check the image's checkbox and submit the update action it will delete all the three images.
Here is my work model's code:
before_save :destroy_image?
belongs_to :category
has_many :images, :as => :assetable, :class_name => "Work::Image", :dependent => :destroy
accepts_nested_attributes_for :images, :reject_if => proc { |attributes| attributes['attachment'].blank? }
def image_delete
#image_delete ||= "0"
end
def image_delete=(value)
#image_delete = value
end
private
def destroy_image?
self.images.clear if #image_delete == "1"
end
end
class Work::Image < Asset
has_attached_file :attachment, :styles => {:small => "200x150>", :large => "400x300>"},
:url => "/uploads/works/:id/:style/:basename.:extension",
:path => ":rails_root/public/uploads/works/:id/:style/:basename.:extension"
end
I think the problem is that delete all images
self.images.clear if #image_delete == "1"
but I don't understand how to fix it. I hope in your help.
You should use the :allow_destroy options of accepts_nested_attributes_for instead.
And in your form something like this:
<% #work.images.each do |image| %>
<%= f.fields_for :images, image do |image_fields| %>
<%= image_tag image.url(:small) %>
<%= image_fields.check_box :_destroy %>
<% end %>
<% end %>