Hello dear Programmers,
I'm trying to develop a web application with the ebook "Praxiswissen - Ruby on Rails". My problem is that I want to save Images through a form to my project directory. The database just saves the name of the pictures with the saving time:
def unique_and_proper_filename(filename)
Time.now.to_i.to_s + '_' + File.basename(filename)
end
My problem is that my pictures dont get saved after submitting my form. I dont get some exceptions, thats why I dont know where my issue is.
Controller:
class PostsController < ApplicationController
require 'will_paginate'
def new
#post = Post.new
end
# information about saving the picture
def create
#post = Post.new(params[:post].permit(:title, :description, :date, :image_file, :thumbnail_file))
# Form isn't correctly filled message
if !#post.valid?
flash.now[:notice] = "Bitte füllen Sie alle Felder aus und überprüfen Sie Ihre Angaben."
render(:action => :new)
# Files weren't saved message
elsif !#post.save_files
flash.now[:notice] = "Es trat ein Fehler beim Hochladen der Dateien auf."
render(:action => :new)
# Files saved correctly message
else
#post.save
flash[:notice] = "Dateien wurden hochgeladen und die Daten wurden gespeichert."
redirect_to(:action => :list)
end
end
# list action for listing my pictures
def list
#posts = Post.paginate(:page => params[:page], :order => "date DESC", :per_page => 15)
#post_pages = Post.paginate(:page => params[:page], :order => "date DESC", :per_page => 15)
end
end
HTML Form:
<h2>Neues Foto anlegen</h2>
<%= form_tag({:action => :create}, :multipart => true) %>
<h3>Bilddaten</h3>
<p>
Titel<br/>
<%= text_field(:post, :title) %>
</p>
<p>
Beschreibungen<br/>
<%= text_field(:post, :description) %>
</p>
<p>
Datum und Uhrzeit<br/>
<%= datetime_select(:post, :date, :order => [:day, :month, :year, :hour]) %>
</p>
<p>
<h3>Datei-Upload</h3>
<p>
Bilddatei:<br/>
<%= file_field(:post, :image_file) %>
</p>
<p>
Thumbnail:<br/>
<%= file_field(:post, :thumbnail_file) %>
</p>
<%= submit_tag("Speichern") %>
</p>
</form>
Model:
class Post < ActiveRecord::Base
validates_presence_of(:title, :description, :date, :image, :thumbnail)
I18n.enforce_available_locales = false
def image_file= (fileobj)
if fileobj.size > 0
#image_file = fileobj
self.image = unique_and_proper_filename(fileobj.original_filename)
end
end
def thumbnail_file= (fileobj)
if fileobj.size > 0
#thumbnail_file = fileobj
self.thumbnail = unique_and_proper_filename(fileobj.original_filename)
end
end
def save_files
# Bilddatei save
if !save_uploaded_file(#image_file, IMAGE_DIR, self.image)
return false
end
# Thumbnail save
if !save_uploaded_file(#thumbnail_file, THUMBNAIL_DIR, self.thumbnail)
return false
end
end
private
def unique_and_proper_filename(filename)
Time.now.to_i.to_s + "_" + File.basename(filename)
end
private
def save_uploaded_file(fileobj, filepath, filename)
# Complete Path
complete_path = Rails.root + "/public/" + filepath
# if neccessary, create directory
FileUtils.mkdir_p(complete_path) unless File.exists?(complete_path)
# save data
begin
f = File.open(complete_path + "/" + filename, "wb")
f.write(fileobj.read)
rescue
return false
ensure
f.close unless f.nil?
end
end
end
I'm only getting the message that there went something wrong with saving the files when i fill the form correctly but it should return a message that says that my file were saved.
I'm sorry for that massive length of my question but I really dont know where my issue is... If there's a need for more information or code, I will add it as fast as I can.
Thank you very much in advance!
Update 17/02/22:
Paperclip has since been deprecated, it is recommended you use Rails' own Active Storage.
Original Answer:
I'm sorry but I'll only be able to recommend what we use:
Paperclip
I appreciate you're using a tutorial, but I'd highly recommend using the Paperclip gem for this
This handles ALL the heavy lifting for you:
#GemFile
gem "paperclip", "~> 4.1.1"
Model
#app/models/post.rb
Class Post < ActiveRecord::Base
has_attached_file :image
end
#migration
add_attachment :posts, :image
Controller
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
end
private
def post_params
params.require(:post).permit(:image, :other, :params)
end
View
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.file_field :image %>
<% end %>
I'm lucky to tell that I found my issue. My save_files method in my Post model doesn't returned true..
I post this answer because maybe someone could use this question as an answer for his own problem. Here's where I added my return true :
def save_files
# Bilddatei save
if !save_uploaded_file(#image_file, IMAGE_DIR, self.image)
return false
end
# Thumbnail save
if !save_uploaded_file(#thumbnail_file, THUMBNAIL_DIR, self.thumbnail)
return false
end
return true # <--------- Need to be added!
end
Try to add enctype= multipart/formdata in your form tag if you are using form to post your data
Related
When I write a message and when pressing the send option,
I want to store student_id, coach_id and message to the database. student_id and coach_id are being saved, but the message field is not being saved. It shows null in the database. How do I fix this?
Any help is appreciated.
Controller file:
class CourseQueriesController <ApplicationController
def index
#course_query = CourseQuery.new
end
def create
# #course_query = CourseQuery.new(course_query_params)
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:message]).first_or_create
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def set_course_query
#course_query = CourseQuery.find(params[:id])
end
# def course_query_params
# params[:course_query].permit(:message)
# end
end
model/course_query.rb:
class CourseQuery < ActiveRecord::Base
belongs_to :student
belongs_to :coach
end
view/course_query/index.html.erb:
<%= simple_form_for (#course_query) do |f| %>
<%= f.button :submit , "Send or press enter"%>
<%= f.input :message %>
<% end %>
database /course_queries:
It seems you didn't permit :course_query.
Try to permit your params the following way:
def course_query_params
params.require(:course_query).permit(:message)
end
But according to the 2nd way you pass params (params[:message]) I think you have a bit different params structure. So try another one:
def course_query_params
params.permit(:message)
end
When you look into the params generated in the log, you will see that the message inside the course_query hash, so params[:message] should be params[:course_query][:message]
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message]).first_or_create
How to use model's properties at Rubi on Rails' views?
My model:
class EntryItems < ActiveRecord::Base
attr_accessible :created_on, :updated_on, :activity_type, :date_from, :date_to
attr_protected :user_id, :from_tyear, :from_tmonth, :from_tday
##date_from = nil
def date_from
##date_from = Date.new(read_attribute(:from_tyear), read_attribute(:from_tmonth), read_attribute(:from_tday))
end
def date_from=(date_from_value)
if date_from_value.is_a?(Time)
##date_from = date_from_value.to_date
end
write_attribute(:from_tyear, date_from_value.year)
write_attribute(:from_tmonth, date_from_value.mon)
write_attribute(:from_tday, date_from_value.mday)
end
end
My controller:
class ItemEntriesSetupController < ApplicationController
unloadable
def index
#item_entry = DayoffEntries.new(:user => User.current, :created_on => Time.now, :updated_on => Time.now)
#item_entry_post_url = url_for(:controller => 'item_entries_setup', :action => 'update')
end
def update
#code is skipped
end
end
My view:
<%= form_tag(#item_entry_post_url) do %>
<dl>
<label>Issues:</label>
<%=date_field_tag 'date_from', true, #item_entry.date_from %>
</dl>
<%= submit_tag(l(:button_create)) %>
<% end %>
It fails with underfined method 'div' for nil:NilClass.
What do I do wrong?
(I'm really new in Ruby on Rails, so I still have problems with finding right practicies, because there're plenty of code examples written n different style).
I've currently five boolean attributes. I do a custom validation on them :
def limit_impacts
i = 0
choices = [self.first_attr, self.sec_attr, self.third_attr, self.fourth_attr, self.fifth_attr]
choices.each do |choice|
if choice == true
i+=1
end
end
if i > 2
errors[:base] << ("There must be one or two impacts.")
end
end
The idea is to test if more than two of them are set to true, if it's the case, set an error.
I'm setting a :base error because it's not related directly to only one attribute.
I'm simply doing this for my validation : validate :limit_impacts
and the part of the view handling this :
= f.input :first_attr, :as => :boolean
= f.input :sec_attr, :as => :boolean
= f.input :third_attr, :as => :boolean
= f.input :fouth_attr, :as => :boolean
= f.input :fifth_attr, :as => :boolean
The problem is, when I check more than 2 checkboxes the entrie is not saving and that's normal, but no error message is showing up in the view.
What am I doing wrong ?
By the way I tested it in rails console :
MyModel.errors[:base]
=> ["There must be one or two impacts."]
And this syntax doesn't work either :
errors.add :base, "message"
EDIT : Here's my controller. It's about the edit method.
def edit
#page_title = t('projects.edit.title')
#project = Project.find(params[:id])
#steps = #project.steps
#rewards = #project.rewards
#project_bearer = #project.user
end
Nothing linked with these attributes.
When I try to create a project via the rails console, it returns me false :
2.0.0p247 :001 > t = Project.create(:social_influence => true, :environmental_influence => true, :economical_influence => true)
=> <Project all my attributes ..>
2.0.0p247 :002 > t.save
(1.2ms) BEGIN
(2.0ms) ROLLBACK
=> false
SOLUTION :
The problem was my update method, bewteen render and redirect. Thanks to #delba I solved it.
If you want to see the solution, there's a discussion in the comments of his answer.
In the view containing the form, make sure you display the errors:
<%= form_for #my_model do |f|
<% if #my_model.errors.any? %>
<ul class="errors">
<% #my_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%# the rest of the form %>
<% end %>
In your controller:
def create
#my_model = MyModel.new(my_model_params)
if #my_model.save
redirect_to 'blabla'
else
render :new
end
end
In your model:
validate :limit_impacts
private
def limit_impacts
if [first_attr, sec_attr, third_attr, fourth_attr, fifth_attr].count(true) > 2
errors[:base] << "There must be one or two impacts."
end
end
Let's start with your validation method:
def limit_impacts
choices = [first_attr, sec_attr, third_attr, fourth_attr, fifth_attr]
errors[:base] << "There must be one or two impacts." if choices.count(true) > 2
end
Much cleaner, isn't it? :)
Could you show us your layout / your view bit which displays errors? I will update answer then.
I am very new to ROR. I have a task to finish:
Here's the Model:
class File::DataImport < ActiveRecord::Base
attr_accessible :created_by, :file_name, :file_source, :updated_at, :updated_by
end
Here's the Controller:
class Files::DataImportsController < ApplicationController
def index
end
def new
end
end
And the views I have are index and new.
I want a field to upload data. The data should be stored in the server and save the filepath into the database in a specified column file_name. The path should be default to all uploading files.
I am stuck with how to start. Please help me to find the solution and I will learn from this.
Thanks in advance.
db/migrate/20110711000004_create_files.rb
class CreateFiles < ActiveRecord::Migration
def change
create_table :files do |t|
t.string :name
# If using MySQL, blobs default to 64k, so we have to give
# an explicit size to extend them
t.binary :data, :limit => 1.megabyte
end
end
end
app/controllers/upload_controller.rb
class UploadController < ApplicationController
def get
#file = File.new
end
end
app/views/upload/get.html.erb
<% form_for(:file,
url: {action: 'save'},
html: {multipart: true}) do |form| %>
Upload your file: <%= form.file_field("uploaded_file") %><br/>
<%= submit_tag("Upload file") %>
<% end %>
app/models/file.rb
class File < ActiveRecord::Base
def uploaded_file=(file_field)
self.name = base_part_of(file_field.original_filename)
self.data = file_field.read
end
def base_part_of(file_name)
File.basename(file_name).gsub(/[^\w._-]/, '')
end
end
app/controllers/upload_controller.rb
def save
#file = File.new(params[:file])
if #file.save
redirect_to(action: 'show', id: #file.id)
else
render(action: :get)
end
end
app/controllers/upload_controller.rb
def file
#file = File.find(params[:id])
send_data(#File.data,
filename: #File.name,
disposition: "inline")
end
app/controllers/upload_controller.rb
def show
#file = File.find(params[:id])
end
app/views/upload/show.html.erb
<h3><%= #file.name %></h3>
<img src="<%= url_for(:action => 'file', :id => #file.id) %>"/>
you should consider using one of the already available solutions like paperclip: https://github.com/thoughtbot/paperclip or carrierwave: https://github.com/jnicklas/carrierwave
Besides the Readmes there are also good tutorials out there:
http://railscasts.com/episodes/134-paperclip
http://railscasts.com/episodes/253-carrierwave-file-uploads
edit: As you want to implement it yourself I recommend examining the sources of the above on Github and try to understand what their code is doing. Also I would not bother implementing it myself, but if you have your reasons this might get you going..
You might want to look into a solution such as carrierwave.
The Github page provides a good explanation on how to use it, but this is also a nice guide.
I'm following the tutorial http://www.funonrails.com/2012/01/csv-file-importexport-in-rails-3.html]for upload files in rails 3, because I need that my app's user could upload csv files but when I tried to save the file I get: uninitialized constant CustomersController::CSV message, before change my routes to get "customers/import" to post "customers/import" I had other error No route matches [POST] "/customers/import" what Im doing wrong? thanks in advance.
MY CONTROLLER:
class CustomersController < ApplicationController
def import
if request.post? && params[:file].present?
infile = params[:file].read
n, errs = 0, []
CSV.parse(infile) do |row|
n += 1
# SKIP: header i.e. first row OR blank row
next if n == 1 or row.join.blank?
# build_from_csv method will map customer attributes &
# build new customer record
customer = Customer.build_from_csv(row)
# Save upon valid
# otherwise collect error records to export
if customer.valid?
customer.save
else
errs << row
end
end
# Export Error file for later upload upon correction
if errs.any?
errFile ="errors_#{Date.today.strftime('%d%b%y')}.csv"
errs.insert(0, Customer.csv_header)
errCSV = CSV.generate do |csv|
errs.each {|row| csv << row}
end
send_data errCSV,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=#{errFile}.csv"
else
flash[:notice] = I18n.t('customer.import.success')
redirect_to import_url #GET
end
end
end
end
MY MODEL:
class Customer < ActiveRecord::Base
scope :active, where(:active => true)
scope :latest, order('created_at desc')
def self.csv_header
"First Name,Last Name,Email,Phone,Mobile, Address, FAX, City".split(',')
end
def self.build_from_csv(row)
# find existing customer from email or create new
cust = find_or_initialize_by_email(row[2])
cust.attributes ={:first_name => row[0],
:last_name => row[1],
:email => row[3],
:phone => row[4],
:mobile => row[5],
:address => row[6],
:fax => row[7],
:city => row[8]}
return cust
end
def to_csv
[first_name, last_name, email, phone, mobile, address, fax, city]
end
end
*MY VIEW:
<h1>Subir Archivos</h1>
<%= form_tag('import', :multipart => true) do %>
<p>
File:<br />
<%= file_field_tag 'file' %><br />
</p>
<p>
<%= submit_tag "subir" %>
</p>
<% end %>
MY ROUTES:
Pruebaupcsv::Application.routes.draw do
post "customers/import"
You need to add a require 'csv' before you use it, either in an initializer, or at the top of your controller.