So, for my Rails project I have two models called ButtonOrderDiv and ButtonFinishDiv. They have a has_many/belongs_to relationship where a button_order_div has many button_finish_divs and each button_finish_div belongs to a button_order_div. I am trying to write code so that when the user creates a new button_order_div, the right number of button_finish_divs are automatically created. There is a field for button_order_divs called number_of_parts, which is the number of button_finish_divs to be created. Right now, this is the code that I have inside the ButtonOrderDiv controller:
def create
#button_order_div = ButtonOrderDiv.new(button_order_div_params)
respond_to do |format|
if #button_order_div.save
number = #button_order_div.number_of_parts
for i in number
ButtonFinishDiv.create(button_order_div_id: #button_order_div.id, part: i)
end
format.html { redirect_to #button_order_div, notice: 'Item criado com sucesso.' }
format.json { render :show, status: :created, location: #button_order_div }
else
format.html { render :new }
format.json { render json: #button_order_div.errors, status: :unprocessable_entity }
end
end
end
As you can see, inside the if #button_order_div.save condition, I am trying to create a new ButtonFinishDiv, but it is giving me the "undefined method `each' for 3:Fixnum" error. How do I make this work?
Numbers (Fixnum) don't have the method each (when you do a for x in y, you are trying to iterate through a number), which is specific to the array data structute. What you want is something like this:
1.upto(number) do |i|
ButtonFinishDiv.create(button_order_div_id: #button_order_div.id, part: i)
end
Related
I have a model called event which attributes are:
string "name"
string "location"
string "lecturer"
date "start_time"
date "end_time"`
I want to take data from icalendar type file and create event instances. How should I do this ? I tried to do a method in events_controller.rb:
def new_method
#ievent_file = File.open("calendar2.ics")
#ievents = Icalendar::Event.parse(#ievent_file)
#ievent = #ievents.first
#ievent = Event.new(name:#ievent.summary,location:#ievent.location, lecturer:#ievent.description, start_time:#ievent.dtstart, end_time:#ievent.dtend)
end
But then what I should do with it ? Should I call this function in view or maybe should I take this code to method called create which looks like this:
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
my understanding calendar2.ics have many records, if you want to loop through all the event inside calendar2.ics and save it to your event table (import ics file to event table)
below are sample steps, inside your routes file create one method
resources :events do
collection {
get :transfer_data_from_ics
}
end
inside your events_controller you create 2 methods
def transfer_data_from_ics
# get data from ics
#ievent_file = File.open("calendar2.ics")
#ievents = Icalendar::Event.parse(#ievent_file)
# loop through
#ievents.each do |i_event|
Event.create(
name: i_event.summary,
location: i_event.location,
lecturer: i_event.description,
start_time: i_event.dtstart,
end_time: i_event.dtend)
end
# route back to events list
redirect_to events_path
end
to get the method called you can create menu / button with link_to as follow:
<%= link_to "Transfer data", transfer_data_from_ics_events_path %>
I have an application with two means of inputting data: (1) a single-entry page / a form_with fields [ aka 'input_page' ] and (2) a file upload page [ aka 'file_upload_page' ] that accepts a spreadsheet with multiple entries (here, student courses).
My goal is to have the validation error messages appear on the view associated with the source of input, either the input_page or the file_upload_page.
The courses controller currently looks like this:
def create
#user = current_user
#course = (Course.import(params[:course][:file]) ||
Course.new(course_params))
# the input is either a file (spreadsheet) or the aforementioned
# `form_with` fields
#course.save
respond_to do |format|
if #course.save
# when successful, both inputs return the same view,
# `courses_path`
format.html { redirect_to courses_path, notice: '...success!' }
format.json { render :show, status: :created, location:
courses_path }
else
# here is where the 'challenge' arises for the single input_page
format.html { redirect_to input_page, alert:
course.errors.full_messages }
# now, how do I *conditionally* return the file_upload_page if
# the user uploaded multiple courses with a spreadsheet
format.html { redirect_to file_upload_page, alert:
course.errors.full_messages }
end
(The uploaded spreadsheet file is parsed using the Roo gem in the courses model, courses are saved, and returned to the courses controller, create action.)
Currently, (1) a successful save of either the input_page or the file_upload_page - correctly - returns the courses_path. (2) If validations fail, both the input_page and the file_upload_page return the input_page view - because that code is run first.
I need something in the else block like 'if input is from X view, return X view with errors, else return Z view with errors'
Your params tell you if it's a file upload or not so use that logic to determine the errors path:
def create
#user = current_user
#course = Course.import(params[:course][:file]) || Course.new(course_params)
if #course.save
respond_to do |format|
format.html { redirect_to courses_path, notice: '...success!' }
format.json { render :show, status: :created, location: courses_path }
end
else
errors_path = params[:course][:file] ? file_upload_page : input_page
respond_to do |format|
format.html { redirect_to errors_path, alert: course.errors.full_messages }
end
end
end
I want to create multiples objects of a same model in Rails and I'm facing some problems saving them. I already add and remove fields dynamically, but I can't figure out how to save the multiples objects.
I'm receiving the data from my View like this:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"0aoRgalvZPKdBJr15EooxNCimh2C6R2RBYi3wTXTpaIwpzb8cNSAH/968932KFscg8eiNPej1x2iYFsaWalVQw==",
"transaction"=>{
"client_id"=>"206",
"invoice_id"=>"1",
"date"=>"07/07/2016",
"value"=>"50",
"description"=>""},
"dates"=>[
"07/08/2016",
"07/09/2016"],
"values"=>[
"49",
"48"],
"commit"=>"Save"}
transaction is the main transaction, dates and values are what difers from the main transaction and the other two transactions.
My create method in TransactionController is like this:
def create
#transaction = Transaction.new(transaction_params)
dates = params['dates']
values = params['values']
if(!dates.nil?)
#transactions_ = []
dates.length.times do |i|
t = Trasanction.create(
client_id: #transaction.client_id,
invoice_id: #transaction.invoice_id,
description: #transaction.description,
date: dates[i],
value: values[i])
#transactions_ << t
end
end
respond_to do |format|
if #transaction.save
#transactions_.each do |t|
t.save
end
format.html { redirect_to #transaction, notice: 'Transaction succefully created.' }
format.json { render :show, status: :created, location: #transaction }
else
format.html { render :new }
format.json { render json: #transaction.errors, status: :unprocessable_entity }
end
end
end
But I'm getting this error:
uninitialized constant TransactionsController::Trasanction
t = Trasanction.create(
Apparently I can't call Transaction.create that way, but I'm almost sure I saw something like that on a tutorial.
I see there is a mistake in your class name. It should be
Transaction.create({})
but, you are referring to it as
Trasanction.create({})
Just change the class name and it should work.
I have a session variable (user_id) that I'd like to include as a foreign key on a record the user is inserting. I have the form values all coming through the form submit to my controller's entity.update(params) method without a problem using the default params definition. That code looks like
def brand_params
#brand_params = params.require(:brand).permit(:name, :brand_type, :profile_id)
end
The update method looks like
if #brand.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.' }
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
Now I'd like to append the :profile_id session variable to the #brand_params and following other threads here, I've tried a setter method:
def set_brand_params(key, val)
if #brand_params != nil
#brand_params[key] = val
end
end
However, calling this, #brand_params is always nil. Trying to directly add to the brand_params hash doesn't work because it's a better method. If there's a better way to meet this (I'd assume common) use case, I'm all ears! Otherwise, I'd like to know why the var is always nil though in this context, at least the brand_params method sees it as defined and with value. I got this solution in Adding a value to ActionController::Parameters on the server side
Here is the update method as requested:
def update
puts "update"
set_brand_params("profile_id", session[:prof])
respond_to do |format|
if #brand.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.' }
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
I'm not agree with merge your data with the params. Because you must permit only the fields you expect your user update. In this case you don't want the user update profile_id on brands, and that is a security best practice.
Then brand_params must be:
def brand_params
#brand_params = params.require(:brand).permit(:name, :brand_type)
end
Your method update may look by this:
def update
#brand = Brand.find(params[:id])
#brand.assign_attributes(profile_id: session[:prof])
respond_to do |format|
if #barnd.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.'}
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
You don't need the method set_brand_params at all.
If this don't do the trick, please publish the entry controller, and I hope we find the issue.
edit: add respond_to.
I'd like the creation of a Window to generate a number of Timeslots that belong to it. It's pretty easy to take an integer from a form object and create that many timeslots, but I can't figure out how to grab the window ID to tell the timeslots who they belong to.
def create
#window = Window.new(window_params)
#window.capacity.times do
if Timeslot.last
then
#newID = Timeslot.last.id += 1
else
#newID = 1
end
#timeslot = Timeslot.new({id: #newID})
#timeslot.created_at = Time.now
#timeslot.window_id = #window.id
#timeslot.save
end
respond_to do |format|
if #window.save
format.html { redirect_to #window, notice: 'Window was successfully created.' }
format.json { render :show, status: :created, location: #window }
else
format.html { render :new }
format.json { render json: #window.errors, status: :unprocessable_entity }
end
end
end
If anyone can point me to a more elegant way to find a new Timeslot ID, I'd appreciate it too.
#window will not have an ID until after it is saved to the database. Therfore, create timeslots after the record is saved. I extracted timeslot creation to its own private method that gets called after save.
def create
#window = Window.new(window_params)
respond_to do |format|
if #window.save
create_timeslots # <-- timeslot creation performed after save
format.html { redirect_to #window, notice: 'Window was successfully created.' }
format.json { render :show, status: :created, location: #window }
else
format.html { render :new }
format.json { render json: #window.errors, status: :unprocessable_entity }
end
end
end
private
def create_timeslots
#window.capacity.times do
Timeslot.create({window_id: #window.id})
end
end
I removed the code which sets timeslot ID and creation time because ActiveRecord does it already. No need to reinvent the wheel.