How to save each value from an array to database in Rails? - ruby-on-rails

I would like to store each value from an array.
For example the form sends me this data:
"attendance"=>{"event_id"=>"6", "member_id"=>["16", "28", "26"]}
I'd like the database to store the data as:
INSERT INTO "attendances" ("event_id", "member_id") VALUES ("6", "16")
INSERT INTO "attendances" ("event_id", "member_id") VALUES ("6", "28")
INSERT INTO "attendances" ("event_id", "member_id") VALUES ("6", "26")
I've tried to use the usual way of inserting data in Rails, but it failed because the member_ids didn't get passed (I've tried to print the member_ids after the Attendance.new(attendance_params)):
def create
#attendance = Attendance.new(attendance_params)
# puts #attendance[:event_id]
# puts #attendance[:member_id] -> Nothing showed up here.
if #attendance.save
flash[:success] = "Successfully created"
redirect_to new_attendance_path
else
#error_msg = #attendance.errors.full_messages
flash[:error] = #error_msg # Prints ["Member must exist"]
redirect_to new_attendance_path
end
end
I've also tried creating a new function in the model to change the Attendance.new but it'll return
NoMethodError - undefined method `new_each' for #<Class:0x000000000d1e7280>: app/controllers/attendances_controller.rb:17:in `create'
This is my current model:
class Attendance < ApplicationRecord
belongs_to :event
belongs_to :member
# def new_each(attendance)
# attendance_event = attenance[:event_id]
# attendance_members = attendance[:member_id]
# I tried to iterate and save each data here.
# end
end
So, how do I save each value from an array input (from the form) and save it to database?
Any answers and comment will be very appreciated.

You are trying to add attendance in bulk, For that you can do 2 things,
use gem bulk_insert, it's very easy to use here is the link https://github.com/jamis/bulk_insert
you need to iterate through all the members and events from params and create a record for each. though it's a very dirty way.
sample code would be like this. you can modify it as per your need.
errors = {}
attendance_params[:member_id].each do |member_id|
attendence = Attendence.new(event_id:attendance_params[:event_id], member_id: member_id)
errors[member_id] = "Could not save attendance, error =
attendance.errors.full_messages" unless
attendence.save
end

Related

Rails saving arrays to separate rows in the DB

Could someone take a look at my code and let me know if there is a better way to do this, or even correct where I'm going wrong please? I am trying to create a new row for each venue and variant.
Example:
venue_ids => ["1","2"], variant_ids=>["10"]
So, I would want to add in a row which has a venue_id of 1, with variant_id of 10. And a venue_id of 2, with variant_id of 10
I got this working, and it's now passing in my two arrays. I think I am almost there I'm not sure the .each is the right way to do it, but I think that I'm on the right track haha. I have it submitting, however, where would I put my #back_bar.save? because this might cause issues as it won't redirect
Thanks in advance.
def create
#back_bar = BackBar.new
#venues = params[:venue_ids]
#productid = params[:product_id]
#variants = params[:variant_ids]
# For each venue we have in the array, grab the ID.
#venues.each do |v|
#back_bar.venue_id = v
# Then for each variant we associate the variant ID with that venue.
#variants.each do |pv|
#back_bar.product_variant_id = pv
# Add in our product_id
#back_bar.product_id = #productid
# Save the venue and variant to the DB.
if #back_bar.save
flash[:success] = "#{#back_bar.product.name} has been added to #{#back_bar.venue.name}'s back bar."
# Redirect to the back bar page
redirect_to back_bars_path
else
flash[:alert] = "A selected variant for #{#back_bar.product.name} is already in #{#back_bar.venue.name}'s back bar."
# Redirect to the product page
redirect_to discoveries_product_path(#back_bar.product_id)
end
end # Variants end
end # Venues end
end
private
def back_bar_params
params.require(:back_bar).permit(:venue_id,
:product_id,
:product_variant_id)
end
as i said in comments
this is untested code and just showing you how it's possible to do with ease.
class BackBar
def self.add_set(vanue_ids, variant_ids)
values = vanue_ids.map{|ven|
variant_ids.map{|var|
"(#{ven},#{var})"
}
}.flatten.join(",")
ActiveRecord::Base.connection.execute("INSERT INTO back_bars VALUES #{values}")
end
end
def create
# use in controller
BackBar.add_set(params[:venue_ids], params[:variant_ids])
# ...
end

what var type to dynamically access Model's attribute from another controller? (Rails 4.2)

Goal: dynamically update another Model's properties (Tracker) from Controller (cards_controller.rb), when cards_controller is running the def update action.
Error I receive : NameError in CardsController#update, and it calls out the 2nd last line in the
def update_tracker(card_attribute) :
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
Perceived problem: I have everything working except that I don't know the appropriate way to 'call' the attribute of Tracker correctly, when using dynamic attributes.
The attribute of the Tracker is an array (using PG as db works fine), I want to
figure out what property has been changed (works)
read the corresponding property array from Tracker's model, and make a local var from it. (works I think, )
push() a new array to the local var. This new array contains the datetime (of now) and, a string (with the value of the updated string of the Card) (works)
updated the Tracker with the correct attribute.
With the following code from the cards_controller.rb
it's the if #card.deck.tracked in the update method that makes the process start
cards_controller.rb
...
def update
#card = Card.find(params[:id])
if #card.deck.tracked
detect_changes
end
if #card.update_attributes(card_params)
if #card.deck.tracked
prop_changed?
end
flash[:success] = "Card info updated."
respond_to do |format|
format.html { render 'show' }
end
else
render 'edit'
end
end
...
private
def detect_changes
#changed = []
#changed << :front if #card.front != params[:card][:front]
#changed << :hint if #card.hint != params[:card][:hint]
#changed << :back if #card.back != params[:card][:back]
end
def prop_changed?
#changed.each do |check|
#changed.include? check
puts "Following property has been changed : #{check}"
update_tracker(check)
end
end
def update_tracker(card_attribute)
tracker_attribute = case card_attribute
when :front; :front_changed
when :back; :back_changed
when :hint; :hint_changed
end
string_tracker_column = tracker_attribute.to_s
#tracker ||= Tracker.find_by(card_id: #card.id)
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
#tracker.update_attribute(tracker_attribute, updated_array)
end
Edit: For clarity here's the app/models/tracker.rb:
class Tracker < ActiveRecord::Base
belongs_to :card
end
Your use of instance_variable_get has been corrected, however this approach is destined to fail because ActiveRecord column values aren't stored as individual instance variables.
You can use
#tracker[string_column_changed]
#card[card_attribute]
To retrieve attribute values by name. If you want to get an association, use public_send. The latter is also useful if there is some accessor wrapping the column value (eg carrierwave)
From your error it seem your issue is this:
#tracker.instance_variable_get("#{string_tracker_column}")
evaluates to this after string interpolation:
#tracker.instance_variable_get("front_changed")
which is incorrect use of instance_variable_get. It needs an # prepended:
#tracker.instance_variable_get("#front_changed")
Seems like using instance_variable_get is unnecessary, though, if you set attr_reader :front_changed on the Tracker model.

How do I iterate through a hash in an objects param hash?

Rails (and coding) rookie here (I'm sure I'm just missing basic syntax structural stuff), I've created a form where the user can add as many field pairs as they like via AJAX (barely). This form will collect column titles for a 'sheet' and the associated data type (int, str... etc). The sheet will have item entries added later by users. I'm trying to make a Sheets controller create method that not only saves the title and description of the sheet, but also adds a record to the 'columns' table with the column title, data type and associated sheet id. When I submit the sheet form, I get the following params in the server terminal:
(sorry I'm not sure how to wrap the code snippet)
{"utf8"=>"✓", "authenticity_token"=>"yMlnfO1EWptkEXp5+9AGCO5C3vHt62EUHoKjdWoUB8I=", "sheet"=>{"title"=>"test 33", "description"=>"Descriptions"}, "column"=>[{"title"=>"1", "type"=>"num"}, {"title"=>"2", "type"=>"int"}, {"title"=>"3", "type"=>"real"}, {"title"=>"fo", "type"=>"no"}], "commit"=>"Save Specsheet!"}
I'm trying to loop through the column hash to create a record on the columns table. Each hash would use the title and type values as entries on the table.
My create method:
def create
#sheet = Sheet.new(sheet_params)
#sheet[:column].each do |key, value|
#column = Column.new
#column[:column_title] = key
#column[:column_data_type] = value
#column.save
end
if #sheet.save
redirect_to #sheet
else
flash[:error] = "Error saving sheet."
render :new
end
end
My error is usually something like this:
undefined method `each' for nil:NilClass
**#sheet[:column].each do |key, value|**
#column = Column.new
#column[:column_title] = key
#column[:column_data_type] = value
So I know I'm messing up referencing the column hash and its key and values. I'm thinking I can .reduce something here? I have no idea. These kinds of basic structural questions don’t really show up with googling, so please let me know what I'm doing wrong, and thank you for reading all of this! Cheers!
WORKING CODE (sorry for weird formatting)
def create
#sheet = Sheet.new(sheet_params)
column_params.each do |value|
#sheet.columns.build(value.permit(:title, :data_type))
end
if #sheet.save
redirect_to #sheet
else
flash[:error] = "Error saving sheet."
render :new
end
end
private
def sheet_params
params.require(:sheet).permit(:title, :description, :created_at, :updated_at, :column)
end
def column_params
params.require(:column)
end
When you are calling #sheet[:column], you are referencing an instance of Sheet instead of the params that you are trying to cycle through.
If you are trying to associate Columns to Sheets in a has_many relationship, you can create Columns like:
column.each do |key, value|
#sheet.columns.new(
column_title: key
column_data_type: value
end
end
(and then save block)
in your controller. Where column are the params. This will indicate that the column belongs to the sheet instance.
If you are trying to make the Column record without the associations, you can do
column.each do |key, value|
Column.new(
column_title: key
column_data_type: value
end
end
(and then save block)
(Both assuming that your fields are named column_title and not just title.)

Updating a HABTM model associations only deletes the associations

Creating new HABTM associations work just fine for me, but each time I try to update them, all the associations get deleted.
This is how my update action looks like.
def update
params[:dish][:cuisine_ids] ||= []
params[:dish][:diet_ids] ||= []
#user = current_user
#dish = #user.dishes_selling.find_by(:id => params[:id])
#dish.cuisines = Cuisine.where(:id => params[:dish][:cuisine_ids]) ### (1) ###
#dish.diets = Diet.where(:id => params[:dish][:diet_ids]) ### (1) ###
if #dish
respond_to do |format|
if #dish.update(dish_params) ### (2) ###
format.html { redirect_to dish_path(#dish), notice: 'Dish was edited.' }
else
format.html { render action: 'edit' }
end
end
else
flash[:error] = "You can only edit your dish!"
render :index
end
end
My dish params looks like so
def dish_params
params.require(:dish).permit(:title, :desc, cuisine_ids: [:id], diet_ids: [:id])
end
If on edit I click on two checkboxes of cuisine, the logs while on step (2) reads this (Note only Delete and no update or Insert for cuisine_dishes table)
(0.1ms) begin transaction
SQL (0.4ms) DELETE FROM "cuisines_dishes" WHERE "cuisines_dishes"."dish_id" = ? AND "cuisines_dishes"."cuisine_id" IN (7, 9) [["dish_id", 61]]
SQL (0.2ms) UPDATE "dishes" SET "desc" = ?, "updated_at" = ? WHERE "dishes"."id" = 61 [["desc", "yema1245"], ["updated_at", "2014-06-12 06:57:36.622811"]]
(5.5ms) commit transaction
Redirected to http://localhost:3000/dishes/61
Completed 302 Found in 48ms (ActiveRecord: 13.7ms)
Question : Why does updating HABTM associations try to delete the association, instead of trying to create new ones if they do not previously exist, and delete if they previously existed but do not any more.
You should try using the << operator instead of = ;
#dish.cuisines << Cuisine.where(:id => params[:dish][:cuisine_ids]).last
This should add and not replace the required ids in both the tables.
I think the answer is born from a question I have:
Why does updating HABTM associations
What do you mean by updating HABTM associations?
HABTM
HABTM associations are a collection of records which are available to add (<<), or remove (.delete). You can't "update" a record with this - you either have to add or delete (mainly because they don't have primary keys.
We do that in this way:
#app/controllers/your_controller.rb
def update
#obj = Model.find params[:id]
#obj2 = Model2.find params[:model_2]
#obj.asssociative_data << #obj2
end
The problem you have is you're passing new data to your habtm association. The only way you can "update" this data is to overwrite all the current collection with the new ids you've posted (which is what I think you're doing)
Fix
The way to fix this will be to make sure you're passing the correct params in your cuisine_ids: [:id], diet_ids: [:id] strong_params method.
I think your problem will be that you're passing :id only. I think Rails expects you to pass :cuisine_ids and :diet_ids, with those arrays populated with numerical data only, like with our code:
def profile_params
params.require(:profile).permit(attributes(Profile), :image, :category_ids)
end
To do this, you'll have to do this with your form:
<%= f.select(:category_ids, Category.all, prompt: "Category") %>

RAILS V2.3 - (redmine) new unique field value

I am writing a little plugin for my company redmine to assign unique documents [progressive] codes
the code I wrote so far works, but I don't think it is multi threads proof as there is a chance two users get the same document code.
I would like to find a way to lock the table while getting the last number and creating the new record with the incremented document number (D9999)
This is the active record:
class Documenti < ActiveRecord::Base
unloadable
def self.nextest
record=self.last
if (record.nil?) then ultimo=sprintf("D0000")
elsif (record.codice.nil?) then ultimo=sprintf("D0000")
else ultimo=record.codice
end
if (/^D[0-9]{4}/ =~ ultimo) == 0 then
c=ultimo.split("D")
p=c[1].to_i + 1
t=sprintf("D%04i",p)
end
return t
end
end
Controller then is like this:
def new
#documenti = Documenti.new
#documenti.codice=Documenti.nextest
respond_to do |format|
format.html # new.html.erb
end
end
so far the nextest value is not saved and another user can take the same value.

Resources