I have a problem trying to create a dynamic save method. This method should work for all children of this parent method. I have set the instance variables to be set dynamically using instance_variable_set and a hash. This is my save method as it exists now:
def save
if DB.execute("SELECT id FROM '#{self.class.name.downcase}s' WHERE id = ?", #id).empty?
DB.execute("INSERT INTO '#{self.class.name.downcase}s' (url, votes, title) VALUES (?, ?, ?)", #url, #votes, #title)
#id = DB.last_insert_row_id
else
DB.execute("UPDATE '#{self.class.name.downcase}s' SET url = ?, votes = ?, title = ? WHERE id = ?", #url, #votes, #title, #id)
end
end
As you can see, the variables currently have a set number (determined by the number of question marks in the query) and set names (determined by the variable names). How do I make it so that it will take any number of instance variables and names and saves them?
Related
I have a variable session[:pet_profile_tags] that returns an array like ["adult", "activity_low", "overweight"].
I then have an SQLite database, called balto.db, which contains the table pet_tips. That table contains 2 columns, "ID" (Integer) and "C1_inclusion" (VARCHAR).
For each row of the pet_tips table, I need to check if the value contained in the C1_inclusion column contains one of the values of the session[:pet_profile_tags] array variable. If that is the case, I need to check the row's ID and store it inside another array variable, named pet_tips.
I tried the below code but I am getting the following error: TypeError - no implicit conversion of String into Integer: index.rb:428:in '[]' , line 428 being if (session[:pet_profile_tags].include?(row["C1_inclusion"].to_s))
# Assign pet tips
pet_tips = []
# Query the pet_tips table
db = SQLite3::Database.open "balto.db"
rows = db.execute("SELECT * FROM pet_tips")
# Iterate through each row of the table
rows.each do |row|
# Check if the row matches the C1_inclusion column
if (session[:pet_profile_tags].include?(row["C1_inclusion"].to_s))
# If the row matches, add the ID to the pet_tips array
pet_tips << row["ID"]
end
end
session[:pet_tips] = pet_tips
db.close
I have been stuck for hours, any help would be really appreciated!
First I tried returning the value of the session[:pet_profile_tags] variable to make sure I was getting the correct array. Then, I made sure to check that the different column and variable names where correctly referenced in my code.
Error
Your error is here: row["C1_inclusion"]
row is an Array of values in column order e.g. [1,"adult"].
Array#[] expects an Integer (index) but you are calling it with a String ("C1_inclusion")
Solutions
To solve this you can
Option 1:
Use Integer indexes based on column order
if (session[:pet_profile_tags].include?(row[1]))
pet_tips << row[0]
end
Option 2: convert the row to Hash:
Always:
db = SQLite3::Database.open "balto.db"
db.results_as_hash
rows = db.execute("SELECT * FROM pet_tips")
rows.each do |row|
if (session[:pet_profile_tags].include?(row["C1_inclusion"]))
Just for this loop:
rows.each_hash do |row|
if (session[:pet_profile_tags].include?(row["C1_inclusion"]))
Option 3: Query just the data you want.
# Query the pet_tips table
db = SQLite3::Database.open "balto.db"
session[:pet_tips] = db.prepare("SELECT ID FROM pet_tips WHERE pet_tips.C1_inclusion IN (?)")
.execute!(session[:pet_profile_tags].map {|s| "'#{s}'"}.join(","))
.flatten
db.close
This uses session[:pet_profile_tags] as a WHERE condition against pet_tips.C1_inclusion and assumes you have control over this variable (e.g. does not perform escaping)
In my Oracle PL/SQL procedure I am trying to update a row like this:
UPDATE personal p
SET p.surname = surname, p.name = name, p."alter" = alter, p.sex = sex, p.jobcode = jobcode, p.year_wage = month_wage * 12
WHERE p.personalnr = personalnr;
COMMIT;
I have added these two statements right after the commit to confirm the code is reached and executed with the right arguments (e.g. here I want to change the name):
DBMS_OUTPUT.put_line('updated ' || name);
DBMS_OUTPUT.put_line('personalnr ' || personalnr);
Now this update-statement is part of a procedure that is called from within another procedure.
However, the changes are not applied and the name will remain the same even tho the update was executed. I have tried to use an exception-handler as well and there doesn't seem to be any exception happening. I can confirm that the WHERE-clause is as intendet. There is one record that matches the predicate.
Now the strange thing:
When I change the code to the example below, an update happens. However it updates every record and not only the one with the right personalnr. Again: the routine is called only once with one personalnr that matches only one entry in the table.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personalnr;
COMMIT;
It is working, but it's updating all rows in the table (or at least, those where personalnr is not null), not just the one you expect.
From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
You have a PL/SQL variable that has the same name as a column. When you do
where p.personalnr = personalnr
you are really doing:
where p.personalnr = p.personalnr
and the same thing happens in the set part; SET p.surname = surname updates the column value to whatever value it had before, not the PL/SQL variable's value. So it looks like the update didn't happen- it actually did, but because everything was set to the same as it's original value it doesn't look like anything happened. (Except - all rows will now have the same year_wage value...)
You can either prefix your variables with the procedure name:
where p.personalnr = my_proc.personalnr
or change the variable names so they don't conflict; it's common to use a short prefix, e.g. l_ for a local variable, or p_ for a passed-in parameter, etc.
where p.personalnr = l_personalnr
Remember to do that for the set part too, or your update still won't appear to do anything.
UPDATE personal p
SET p.surname = l_surname, p.name = l_name, p."alter" = l_alter,
p.sex = l_sex, p.jobcode = l_jobcode, p.year_wage = l_month_wage * 12
WHERE p.personalnr = l_personalnr;
You need to change the parameter name something other than the table's column name.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personally;
-- here condition is column_name = column_name
-- This will be true for all the records of the table
Change personalnr --> p_personalnr and it will work for you
I have the following OpenERP 7 method:
# Function to get the vat number (CIF/NIF) and then show it on invoice form view
def _get_vat_num(self, cr, uid, ids, field_name, args=None, context=None):
partner_pool = self.pool.get('res.partner')
invoice_pool = self.pool.get('account.invoice')
res = {}
for inv in self.browse(cr, uid, ids, context=context):
invoice = invoice_pool.browse(cr,uid, inv.id, context=None)
partner = partner_pool.browse(cr, uid, invoice.partner_id.id, context=None)
res[inv.id] = partner.vat
return res
inv_vat = fields.Char(compute='_get_vat_num', string="CIF/NIF")
I need to rewrite it to Odoo v8 syntax. I have tried but it doesn't work:
def _get_vat_num(self):
partner_pool = self.env['res.partner']
invoice_pool = self.env['account.invoice']
res = {}
for inv in self.browse(self.id):
invoice = invoice_pool.browse(inv.id)
partner = partner_pool.browse(invoice.partner_id.id)
res[inv.id] = partner.vat
return res
What should be the correct code?
It looks like you're setting a functional field. You should instead be able to define the field as a related field like so:
inv_vat = fields.Char(string="VAT", related="partner_id.vat")
If you really want it as a functional field, this is how you would do it
inv_vat = fields.Char(string="VAT", compute="_get_vat_num")
def _get_vat_num(self):
# self is a recordset of account.invoice records
for invoice in self:
# to set a functional field, you just assign it
invoice.inv_vat = invoice.partner_id.vat
Check out the recordset documentation: https://www.odoo.com/documentation/8.0/reference/orm.html#recordsets
And the computed fields documentation:
https://www.odoo.com/documentation/8.0/reference/orm.html#computed-fields
I'm struggling to loop through an "INSERT INTO" sqlite statement within a Ruby method. Please advice. My code is shown below. The error messages seem to suggest that Ruby doesn't recognise the db object within the method?? Apologies for being a bit of a noob but I've trawled through the internet and I couldn't find the answer :(
require "sqlite3"
require "nokogiri"
begin
db = SQLite3::Database.new('RM.db')
db.execute "CREATE TABLE IF NOT EXISTS Properties(address TEXT, askingPrice TEXT)"
def get_property_list(newpage, dbname)
resultspage = Nokogiri::HTML(open(newpage))
details = resultspage.css("div.details.clearfix")
count_items = details.count
puts "there are #{count_items} items on this page"
for i in 0..count_items-1
address = resultspage.css("span.displayaddress")[i]
asking_price = resultspage.css("p.price")[i]
puts address.text
puts asking_price.text
dbname.execute "INSERT INTO Properties VALUES(#{address}, #{asking_price})"
end
end
get_property_list("someurl.com", db)
rows = db.execute("select * from Properties")
p rows
ensure
db.close if db
end
You're not quoting either value in your INSERT. You shouldn't be using string interpolation for SQL at all, use bound parameters and always specify the column names too:
dbname.execute 'INSERT INTO Properties (address, askingPrice) VALUES (?, ?)', address, asking_price
Or you could prepare a statement and use it over and over again:
insert = dbname.prepare('INSERT INTO Properties (address, askingPrice) VALUES (?, ?)')
for i in 0..count_items-1
#...
insert.execute(address, asking_price)
end
For Sqilte3 C API, I would use sqlite3_last_insert_rowid. How to get this id when using ActiveRecord after insert a new record? I use following way to insert a new record :
Section.new |s|
s.a = 1
s.b = 2
#I expected the return value of save to be the last_insert_id, but it is NOT
s.save
end
'save' method returns true or false based on if saving was successful or not
try smth like:
new_section = Section.new do |s|
s.a = 1
s.b = 2
s.save
end
p new_section
it should print all fields you set manually plus any automatically filled values, including last_insert_id being usually the record 'id'
Assuming you've used the ActiveRecord default field name to hold the primary key, then s.id will hold the id of the record you've just saved.