Multiple paperclip attachments get saved without specifying a file input - ruby-on-rails

I have a User model which has a has_many association of Asset model(standard paperclip setup). In the view, I use fields_for helper to setup a number of file fields for the assets. When the end user does not specify a file, the asset records will not be saved. Which is what I want. So far so good.
Then I added a caption attribute to Asset model. I also put a text field to each corresponding file field. Here comes the problem. When the end user does not specify a file or a caption, the asset records will be SAVED. The saved record has a caption of empty string, with all other paperclip attributs being nil.
The question is how can I prevent the asset record being saved when there is no file assigned to paperclip attributes? And since the assets are optional, I don't want any error feedback generated. Any ideas? Thanks.

You could do a validates_presence_of :caption in your Asset model, but that also makes captions required. How about this checking for the presence of a file on all assets linked to the User before_validation? Something like this maybe? (might need some tweaking)
class User < AR::Base
has_many :assets, :dependent => :destroy
before_validation :check_assets
def check_assets
self.assets.each do |asset|
unless asset.attachment.file?
if asset.new_record?
self.assets.delete(asset)
else
asset.destroy
end
end
end
end
end

Related

Rails admin gem showing s3 attachments which is IMAGES/PDF/DOCS/XLSX

I am using rails_admin gem for the rails api application for the backend admin side.
I am using rails 6 with active_storage and storing attachments on the S3.
On the admin side, I need to display the list of attachments which might be images or files anything.
My question is How to show those in index method, do I need to show images then what to show in pdf/docs, do I need to show the only the link of s3?
currently, it looks like these broken images are the images and other one were files
My model
class Attachment < AttachmentBlock::ApplicationRecord
self.table_name = :attachments
include Wisper::Publisher
has_one_attached :attachment
belongs_to :account, class_name: 'AccountBlock::Account'
has_and_belongs_to_many :orders , class_name: 'BxBlockOrdermanagement::Order'
scope :not_expired, -> {where('is_expired = ?',false)}
end
What should I use here to list the attachment that the user upload?
how to check the attachment type and then if its image then shows the image and if it files then show file url from s3?
thanks.
Yours is a two part question:
To add links to the side menu on rails admin you need to define models so if you wanted an index for all the attachments of type 'pdf' you could use rails STI (single table inheritance) or define a custom default_scope like this:
class ImageAttachment < Attachment
def self.default_scope
where('attachments.attachment LIKE ?', '%png') }
end
end
To customize the row of each individual attachment record you need to defined that behavior for the field on the model rails admin config.
For example you can put this block of code inside your ImageAttachment class.
class ImageAttachment < Attachment
def self.default_scope
where('attachments.attachment LIKE ?', '%png') }
end
rails_admin do
configure :attachment do
view = bindings[:view]
attachment = bindings[:object]
if view
view.content_tag(:img, attachment.attachment.url)
else
''
end
end
list do
field :attachment
end
end
end
As you can see inside the attachment field configuration block you have access to the attachment object and of course all its methods, you can determine the type of attachment and do some special html rendering.

How Do I get the class to which the current class belongs to?

I have an uploader (carrierewave) to save several files, but I'm afraid that some days, some files will have the same name and will cause a problem. Moreover, I'd want the folders to keep a semblance of organization.
So, I have a first scaffold, "magazine" that allows me to create a magazine with its title and several images. The second scaffold that I use, "page", allows the multi upload system.
With this method, my models look like this:
magazine.rb
has_many :pages, :inverse_of => :magazine, :dependent => :destroy
pages.rb
belongs_to :magazine
Now in my uploader, I have the following:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}"
end
which creates folders like this : "uploads / page / image", then uploads every single images inside it, even thought if it's for an other magazine, so the images get mixed up.
My question is the following: is there a way to get to write the magazine's ID instead ? This implies to find the class "pages" belongs to, but I didn't find anything answering my question on google.
Thank you in advance
If you have a custom uploader mounted in the model Page you could access the model attributes normally
PageImageUploader.rb
def store_dir
"uploads/magazines/#{model.magazine.id}/#{model.id}"
end
However, you can't access the models IDs unless your models are persisted. A workaround for that can be find here.

paperclip where does it store file id

I'm new to ror and paperclip.
I use paperclip to upload file, and I wonder how does paperclip determine the file id. For example in my User model, I add paperclip attachment "has_attachment_file", then I find that in database(I use sqlite3) there are four new columns in table "User" including file name, file type, uploaded time and file size. However I can't find the file id this column as I can retrieve from user.file.id. Where does paperclip store this things?
The attached file isn't stored in relation to the User — it's stored directly on it. That's why your User table has the extra columns, and why the file doesn't have an id.
If you want a User to have many files you will need to model them separately and use Rails' has_many. Something like:
class User < ActiveRecord::Base
has_many :images
end
class Image < ActiveRecord::Base
has_attached_file :file
belongs_to :user
end

Limit number of images - Paperclip

I have User model with the following association:
class User < ActiveRecord
has_many :pictures, :as => :imageable
accepts_nested_attributes :pictures
end
My Picture model looks like this:
class Picture < ActiveRecord
belongs_to :imageable, :polymorphic => true
has_attached_file :image
end
Now, I want to user to be able to upload maximum 5 images. And he will select 1 image as his avatar. Now, user can upload images but I don't know how to limit the maximum number of pictures. One more thing, user needs to be able to change his avatar image. How can I achieve this?
In my view, I use input file with name user[picture_attributes][0][image] in order to allow user to change the first picture but it keeps inserting new pictures into database instead of replacing the first picture.
Please help me on this.
Thanks in advance
For the first part of the problem you have, i would suggest you use rails built-in counter_cache method.
Your picture model would thus become:
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true, counter_cache: true
has_attached_file :image
end
Also you would need to add a column called pictures_count to the User model.
This way in your controller you could check if the count is upto 5 records and therefore inform them that they have uploaded the maximum allowed.
if #user.pictures.size == 5 #sorry no more uploads
For the second part of the problem. Is the form action pointed to the new/create action or to your update action. If pointed to the new action a new record would be created but if pointed to the update action then it should change the first image record as you expect.
#charinten For the second part of the problem, some suggestions:
You could try making the id of the pictures accessible in the user model. This way when you try to point to the image to use as an avatar, rails uses that id to update the record. If you try to update the record without pointing rails to that id, it would assume you are trying to create a new record.
Also rather than using user[picture_attributes][0][image] you could in your user profile. Find a specific image and point to that image on the edit action.
Hope this helps

Validate nested object only if the main object requires its presence

Here's my model hierarchy:
Page
has_many :media (media can be TextMedium, ImageMedium, VideoMedium... (STI))
each media has_one :attachment (ImageAttachment, VideoAttachment... (different tables))
I want to have a form to create a page with:
an ImageMedium (not required)
a VideoMedium (not required)
other attributes...
An ImageMedium validates the presence of its attachment.
Same for a VideoMedium.
But the page don't validates the presence of an ImageMedium or VideoMedium media.
My form looks something like this ("pseudo code"):
form_for page
fields_for :image_media
fields_for :attachement
image_field
fields_for :video_media
fields_for :attachement
video_field
PROBLEM:
if I fill in the form completely, it works great, and I can create the page with its media and attachments.
but if I don't fill in the video field for instance, I would still like my page to be created (because the video media is not required by the page). BUT it won't be, because the video media requires the presence of its attachment, so it's invalid, so the page is invalid...
QUESTION:
How can I ignore the fact that the video media validation fails and still create the page?
EDIT:
i found an answer:
class Page < ActiveRecord::Base
accepts_nested_attributes_for :video_media, reject_if: ->(attributes) { attributes["attachment_attributes"]["video"].blank? }
end
If you have a better one, please share.
I found an answer:
class Page < ActiveRecord::Base
accepts_nested_attributes_for :video_media, reject_if: ->(attributes) { attributes["attachment_attributes"]["video"].blank? }
end
If you have a better one, please share.

Resources