In my rails project I use Carrierwave to upload images to S3 via fog. So far I have the Create Read and Delete portions of the CRUD spectrum working.
My problem is the edit/update portion. Im use the same _form.html.erb to edit that I used for creating records. When I click the edit link the form loads all of my data into the form fields for editing with the exception of the image. The form field is blank as though there is no image associated with the record.
How do I update an image that is already saved to S3?
Models/listing.rb
class Listing < ActiveRecord::Base
attr_accessible :body, :price, :title, :image
mount_uploader :image, ListingUploader
end
Controllers/listings_controller.rb (edit/update portion)
def edit
#listing = Listing.find(params[:id])
end
def update
#listing = Listing.find(params[:id])
if #listing.update_attributes(params [:listing])
redirect_to :action => 'show', :id => #listing
else
render :action => 'edit'
end
end
_form.html.erb
<%= form_for #listing, :html => { :multipart => true } do |l| %>
<p>
<%= l.label :title %>
<%= l.text_field :title %>
</p>
<p>
<%= l.label :price %>
<%= l.text_field :price %>
</p>
<p>
<label>Upload a Picture</label>
<%= l.file_field :image %>
<%= l.hidden_field :image_cache %>
</p>
<div class="image-pre">
<%= image_tag(#listing.image_url(:thumb)) if #listing.image? %>
</div>
<p>
<%= l.label :body %>
<%= l.text_area :body, :class => "tinymce" %>
<%= tinymce %>
</p>
<%= l.submit %>
<% end %>
In your listings_controller.rb, try something like:
def edit
#listing = Listing.find(params[:id])
#listing.image.cache!
end
Haven't tested this out myself, but I think it might work, given that image_cache field is used to circumvent this problem normally.
I had upgraded my app to Rails 6 sometime ago, then started experiencing this issue.
tldr; you need a carrierwave gem version 2.1.0 or higher for Rails 5.0 and up.
I used bundle outdated --strict to see if carrierwave could be upgraded. Check Gemfile to see if the requested version is set correctly. Also check Gemfile.lock to see if other gems are holding it back; you'll need to upgrade them as well. Then I used bundle update --strict to perform the actual upgrade.
Related
I am relatively new to rails development and would appreciate some guidance.
I have an application where I am importing data from a CSV file. My goal is to assign a value to the imported CSV (Move ID) to group all the imported data.
For example (what I currently have working). I have a group of employees needing to move to a new location, I have a table called Move which has_many employee_moves (another table). I have a collection_select where I can pick from Move_IDs that exist. I can also import a CSV file where I am assigning each row to my existing Employee_Move table.
I would like, if possible, to be able to select a Move_ID, select my CSV file to import and associate each record from the CSV file to my selected Move_ID. Essentially assigning that CSV of employees moving to a specific move.
I am able to assign these values manually one at a time once imported in a edit view using a partial I have created.
<%= render 'form' %>
I hope that this is all making sense...
I have been trying to solve this for a while now so please excuse (and feel free to point out) any redundant code that may be remaining from my previous attempts.
EmployeeMove Model
require 'csv'
attr_accessor :move
belongs_to :move, optional: true
belongs_to :location, optional: true
def self.import(file)
CSV.foreach(file.path, headers: false) do |row|
EmployeeMove.create!({
:person => row[1],
:from_building => row[2],
:from_floor => row[3],
:from_room => row[4],
:to_building => row[5],
:to_floor => row[6],
:to_room => row[7],
:notes => row[8],
:move_id => move.id <-- I know this is my problem.
})
end
end
EmployeeMovesController
class EmployeeMovesController < ApplicationController
def index
#employee_moves = EmployeeMove.all
end
def new
#employee_move = EmployeeMove.new
end
def create
#employee_move = EmployeeMove.new(employee_move_params)
if #employee_move.save
flash[:notice] = "Employee Move successfully added"
redirect_to employee_move_path(#employee_move)
else
render 'new'
end
end
def show
#employee_move = EmployeeMove.find(params[:id])
end
def edit
#employee_move = EmployeeMove.find(params[:id])
end
def update
#employee_move = EmployeeMove.find(params[:id])
if #employee_move.update(employee_move_params)
flash[:notice] = "Emplyee Move successfully updated"
redirect_to employee_move_path(#employee_move)
else
render 'edit'
end
end
def import
EmployeeMove.import(params[:file])
redirect_to employee_moves_path, notice: "CSV Imported."
end
private
def employee_move_params
params.require(:employee_move).permit(:id, :person, :from_building, :from_floor, :from_room, :to_building, :to_floor, :to_room, :comments, :notes,
:Complete, :move_id, :location_id)
end
end
My Collection_Select
Currently located in the new and edit pages of Employee Move, also using a _form partial.
<%= form_for #employee_move do |f| %>
<p>
<%= f.label :Move_ID %> 
<%= collection_select(:employee_move, :move_id, Move.all, :id, :id, { :prompt => true }, { :multiple => false} ) %>
</p>
<p>
<%= f.label :employee_move_name %> 
<%= f.text_field :person %>
</p>
<p>
<%= f.label :From_Building %> 
<%= f.text_field :from_building %>
</p>
<p>
<%= f.label :From_Floor %> 
<%= f.text_field :from_floor %>
</p>
<p>
<%= f.label :From_Room %> 
<%= f.text_field :from_room %>
</p>
<p>
<%= f.label :To_Building %> 
<%= f.text_field :to_building %>
</p>
<p>
<%= f.label :To_Floor %> 
<%= f.text_field :to_floor %>
</p>
<p>
<%= f.label :To_Room %> 
<%= f.text_field :to_room %>
</p>
<p>
<%= f.label :Comment %> 
<%= f.text_field :comments %>
</p>
<p>
<%= f.label :Note %> 
<%= f.text_field :notes %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
A thought while writing this, I would prefer for my CSV import to be located in my Move show page (not meaning Employee Move show page). I currently have a Move index page where I can select from a list of created moves. Selecting a move from this list I can see the move details followed by a table populated with every Employee part of that move. This is currently working, but like I stated before, only if I manually assign the Move_ID to an employee after the CSV file has been imported.
=======================================================
Update
Thanks Simple Lime for the help thus far. The following are the suggested changes. As I mention in my comment below, I get a "undefined method 'import' for # <Move:0x00000004388a20>" currently. These are the changes I have made since my original post.
Import Method
I have moved my import method from my EmployeeMoveController to my MoveController.
def import
#move = Move.find(params[:move_id])
#move.import(params[:file])
redirect_to moves_path(#move), notice: "CSV Imported."
end
Rake Routes
import_moves POST /moves/import(.:format) moves#import
move_import GET /moves/:move_id/import(.:format) moves#import
I manually added the second route "move_import", but since the original (import_moves) also points to the same Controller#Action, maybe there is some form of conflict happening here? I'm not sure.
The following code in my Move show page correctly directs me to the correct URL, /moves/1/import.
<%= link_to "Add Employees to Move", move_import_path(#move.id) %>
Employee Move Model
I have removed the :move_id => move.id.
class EmployeeMove < ActiveRecord::Base
require 'csv'
attr_accessor :move
belongs_to :move, optional: true
belongs_to :location, optional: true
def self.import(file)
CSV.foreach(file.path, headers: false) do |row|
self.employee_moves.create!({
:person => row[1],
:from_building => row[2],
:from_floor => row[3],
:from_room => row[4],
:to_building => row[5],
:to_floor => row[6],
:to_room => row[7],
:notes => row[8]
})
end
end
end
Import View (html)
My import.html.erb file is located in /views/employee_moves.
<div class="col-md-6">
<div class="panel panel-default ">
<div class="panel-body">
<h1>move</h1>
<h1>Move</h1>
<p>
Move ID:
<%= #move.id if #move%>
</p>
<%= form_tag import_employee_moves_path, multipart: true do %>
<%= file_field_tag :file %>
<%= submit_tag "Import" %>
<% end %>
<%= link_to "All Moves", moves_path %>
</div>
</div>
</div>
I hope that this update explains well enough the current state of my problem. Please let me know if anymore information would be required.
Again! Thank you for all the help and patience with my learning.
============================================================
Final Changes that got this working me for me
I moved my self.import from my Employee_Move model to my Move model and removed self, making it an instance method.
I changed the get 'import' in my routes.rb to post 'import'
Hope this is of help to anyone with the problem I've had. Thanks again to Simple Lime for all the help!
So, assuming I understand everything correctly, you have a 2 models Move and EmployeeMove. You want to be able to upload a CSV from the Move show page and parse that CSV, creating an EmployeeMove record for each row in the CSV and the Move whose page you are currently on.
To do this, I would first move EmployeeMove.import into Move#import (so, an instance method on the Move model instead of a class method on EmployeeMove). Then, since you mention Move has_many employee_moves, instead of EmployeeMove.create!({...}), you can do
self.employee_moves.create!({...})
and not need the :move_id => move.id line in the create anymore
Lastly, move the import method from the EmployeeMovesController into MovesController and change the route in config/routes.rb to look more like /moves/:id/import (the only important part here is that it goes to the MovesController and has an :id for which move it's importing to. Then your import method on the controller should look something like
def import
#move = Move.find(params[:id])
#move.import(params[:file]) # been a while since I've done a file upload, but I assume params[:file] is working here
redirect_to moves_path(#move) # or wherever you'd like to send them
end
and that should do it for ya. Let me know if I misunderstood your problem or you want something clarified further.
I am very new to Rails and I am facing a somehow weird problem, and my googling didn't helped me so far...
I have implemented a classical CRUD ressource following the Getting Started with Rails guide and I am blocking on the "update" part:
http://guides.rubyonrails.org/getting_started.html#updating-articles
This is part of my model "Devwork":
class Devwork < ActiveRecord::Base
validates :short_title, presence: true, uniqueness: true
validates :title_fr, presence: true, allow_blank: false
translates :title, :summary, :description
globalize_accessors
end
I am using the Globalize gem to persist localized data, and Globalize-accessor for the helpers.
Here is the controller's update action:
class DevworksController < ApplicationController
def update
#devwork = Devwork.find(params[:id])
if #devwork.update(devwork_params)
redirect_to #devwork
else
render :edit
end
end
private
def devwork_params
params.require(:devwork)
.permit!
end
end
And part of the form:
<%= form_for #devwork do |f| %>
<p>
<%= f.label :short_title %>
<%= f.text_field :short_title %>
</p>
<p>
<%= f.label :title_fr %>
<%= f.text_field :title_fr %>
<%= f.label :title_en %>
<%= f.text_field :title_en %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
short_title and title_fr are mandatory, while there exist another field title_en which is not. I want the update form to be shown again if the update fails (typically because of empty title_fr).
But it doesn't work. The update never fails (never entering render :edit) even if title_fr is empty. In fact, the update does nothing if one of the field is empty, it only updates non-empty fields.
I surely missed something somewhere, but I can't figure it out... perhaps a missuses of Globalize ?
Thanks for your help !
I have an issue retrieving my file upload information. I am just starting to learn Rails.
I am using ruby 2.0.0p0
And Rails 4.0.0.beta1
Here is my form:
<%= form_for(#person, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :photo %><br />
<%= f.file_field :photo %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And in my person.rb model:
def photo=(file_data)
logger.debug("PHOTO")
logger.debug(file_data)
logger.debug("END OUTPUT PHOTO")
unless file_data.blank?
#file_data = file_data
self.extension = file_data.original_filename.split('.').last.downcase
end
end
I can see in my console that nothing happens (no "PHOTO" output), the photo method is never called.
Why is that?
When I looked in the console I also saw this line that got me worried:
Unpermitted parameters: photo
What does that mean?
In your controller, where you're dealing with params, you need to use .permit to list the attributes the form is allowed to post:
#person = Person.new(params.require(:person).permit(:photo))
Alternatively, if you used Rails' scaffolding generator, you might instead have a person_params method where you would need to add the :photo attribute:
def person_params
params.require(:person).permit(:photo, :name, etc...)
end
I came across file uploading problem in Rails. I found file_field :file helper, that can be used with form_for(#some_model). However, I cannot find any usage for this case, as those tags are used to create/edit some model, by mass assigning. There is, AFAIK, no possibility to treat fileupload as typical field ( See File upload won't work in Ruby on Rails 3 using Multipart Form ). In such a case, manual operation on uploaded file is required. So, why would someone even want to puts a fileupload as a part of model editing?
photo.rb
class Photo < ActiveRecord::Base
attr_accessible :name, :filename,
end
photo_form.html.erb
<%= form_for(#photo, :multipart => true) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.file_field :file %>
<%= f.submit %>
<% end %>
photos_controller.rb
def create
#photo = Photo.new(params[:photo])
line above fails, because theres no :file attribute. It must be handled before and manually removed from :params. Once more - is there any real usage for such tags?
I will will you an example how I am using it I think it explains itself good enough, I hope this helps
<%= form_for #item do |f|%>
<%= f.file_field :photo, accept: 'image/png,image/jpeg'%>
<% end %>
Let me know if you have any doubts
I remember that I used this to upload a xml file in Rails
view:
<%= form_tag({action: :upload}, multipart: true) do %>
<%= file_field_tag 'xml_file' %>
<%= submit_tag 'Submit' %>
<% end %>
controller:
def upload
file_data = params[:xml_file]
end
It is using form_tag but it would not be hard to add other info into that form also.
Rails 2.3.11
I would like to use a text_field to input data under one condition, otherwise using a selection box. Right now, my code looks like this:
views/posters/new.html.erb
<% form_for #poster, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :image %> - We're not going to enlarge it for you, so please upload the biggest copy you can!<br />
<%= f.file_field :image %><br />
</p>
<p>
<% if current_user.admin? && params[:event_id] && !current_user.events.find_by_id(params[:event_id]) && Event.find_by_id(params[:event_id]) %>
<%= f.label "Event ID" %><br />
<%= f.text_field :event_id, :value => params[:event_id] %>
<% else %>
<%= f.label :event_id %><br />
<%= f.select :event_id, #events, :selected => params[:event_id].to_i %>
<% end %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
controllers/posters_controller.rb
def new
#poster = Poster.new
current_user ||= User.find_by_id session[:user_id]
#events = [["Don't attach to an event", '']]
current_user.events.each {|event| #events << [event.title, event.id]}
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #poster }
end
end
Error message: http://cl1p.net/halp
How can I use two different types of form input fields for the same parameter (but each under a different condition, not simultaneously)?
Update: I think the problem stems from the issue of Rails putting the previously-submitted information back into their respective input fields. This explains why no tantrum is thrown when a file that passes all the validation tests (that is, a PNG less than 3 MB), but breaks down when nothing (or anything that doesn't meet that condition) is attached.
Firstly, I don't understand why you are manually setting the values on the text box and select box. Usually, Rails does this for you, but without the form definition, I can't tell if you really need to do this.
Even then, given the information, I think it's safe to say that what you need is this:
f.text_field :event_id
...
f.select :event_id, #events
This ought to work for what you intend to do. I'm not sure what it has to do with the submitting of a file, but yes you are right about the previously-submitted part. The unexpected nil stems from this:
params[:event_id].to_i
Unless you are setting this parameter entry manually inside your controller, you will not be able to cast it to an integer if it is nil. If you go with using the basic form helper calls this goes away.