I have a simple model which I have to set the database name manually on.
Also since it is using an oracle database, I'm setting the sequence name so I can have auto incrementing id's.
When I run the rails console and try to create my model, it comes back and says that the sequence cannot be found. The weird part is the sequence it cannot find is not the sequence that I set in set_sequence_name.
Model
class Survey < ActiveRecord::Base
set_sequence_name "SURVEY.SQ_SURVEY_ID"
set_table_name "SURVEY.SURVEYS"
end
Console error
ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: ORA-02289:
sequence does not exist: select SURVEY.SURVEYS_seq.nextval id from dual
It looks like its ignoring my set sequence name line.
Am I just missing something?
FWIW:
Using 11g I got away with:
self.id = ActiveRecord::Base.connection.execute("select SURVEY.SQ_SURVEY_ID.nextval id from dual").fetch
It appears that in my case, the sequence returns a cursor on which I need to do a fetch on.
11g/Rails 3.1
Clarifying, this works for oracle 10g
So as far as I can tell, this is a bug in the jdbc adapter (see here http://kenai.com/jira/browse/ACTIVERECORD_JDBC-133). For a work around I'm setting the id manually with a before create filter like this:
class Survey < ActiveRecord::Base
set_table_name "SURVEY.SURVEYS"
before_create do
#since we can't use the normal set sequence name we have to set the primary key manually
#so the execute command return an array of hashes,
#so we grab the first one and get the nextval column from it and set it on id
self.id = ActiveRecord::Base.connection.execute("select SURVEY.SQ_SURVEY_ID.nextval id from dual")[0]["id"]
end
end
Related
I am trying to calculate values from existing table column and use it on an external variable.
Let my table columns be as : ["id","unit_price","quantity","extras_1","extras_2"]
I am presenting what i want to do in rails using sql command as reference.
SQL Command:
SELECT unit_price*quantity AS "regular_price",
unit_price*quantity-unit_price*quantity*discount AS "price_after_discount"
FROM order_details;
In Rails Active Record Query i tried same as:
OrderDetail.select('unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount')
From the above query.i tried sorting based on derived attributes.it worked perfectly.But i cannot see the derived attribute values by querying.
The output i got is without the derived attributes as:
[#<OrderDetail >,#<OrderDetail >]
But i need output as:
[#<OrderDetail regular_price: 43, price_after_discount: 54>,#<OrderDetail regular_price: 54, price_after_discount: 76>]
I tried below query to sort the data.It sorted the data perfectly:
OrderDetail.select('unit_price,quantity,unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount').order('regular_price desc')
I can access the values using below command:
OrderDetail.select('unit_price,quantity,unit_price*quantity AS extras_1,unit_price*quantity-unit_price*quantity*discount AS extras_2')
above commmand worked because extras_1 and extras_2 are table columns.
But it is working when assigned to existing table column.I need derived attribute to be non-existing table column name.
How can i access a derived attributes values from a record.I can access them by assigning them to an existing table column.But i want the attributes name given how i want irrespective of table columns.
You will not be able to see derived(alias) attributes. But they are present.
OrderDetail.select('unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount').first.regular_price
Will print regular_price.
What you see in rails console is output of inspect method. inspect method is not implemented to show alias attributes. Hence the confusion arises.
Go through this doc: http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
If an alias was specified, it will be accessible from the resulting
objects:
Model.select('field AS field_one').first.field_one
#=> "value"
Your attributes are perfectly accessible. Just call them! :)
As an additional remark I'd suggest you to use a more modern way of writing the query:
class OrderDetail
scope :with_calculated_prices, -> do
regular_price = (arel_table[:unit_price] * arel_table[:quantity])
price_after_discount = (regular_price - regular_price * arel_table[:discount])
select(regular_price.as('regular_price'), price_after_discount.as('price_after_discount'))}
end
price_after_discount is better defined as
price_after_discount = (regular_price * (1 - arel_table[:discount]))
The problem:
My issues currently have 3 custom fields, let's say FieldA (select list), FieldB (irrelevant), and FieldC (text).
What needs to happen is that on save, FieldC takes the value of <FieldA>-<date in Ymd>-<number from database>
As an example, let's assume that FieldA has the value "Test", and today is the 8th of Jan 2015. FieldC should then be Test-20150108-001 where 001 comes from a custom table in the database, that's unique per value of FieldA, and resets every year.
What I've done to now:
I've used the command line script to generate a plugin via
ruby script/rails generate redmine_plugin subticket
And a model via
ruby script/rails generate redmine_plugin_model subticket subticket_ids fa:string lastnum:integer year:integer
( where fa is the value of FieldA, lastnum is the last number used for that value, and year is the current year for which the lastnum is applicable ).
I've then proceeded to prepend init.rb to add a hook listener in order to implenent the presave hooks:
require_dependency 'subticket_hooks'
And created the file lib/subticket_hooks.rb with the following content:
class SubticketHooksListener < Redmine::Hook::ViewListener
def controller_issues_edit_before_save(context={})
issue = context[:issue]
end
end
However, I cannot find any documentation on how to access/write the value of a custom field here. Still working on making the model work as well, but I assume the documentation is clear enough on that for me to experiment (of course any info is welcomed!)
Do note that this is way beyond my abilities since my core expertise is in a completely different language - take it slow!
I had the same task
My solution:
Every customizable redmine object has custom_field_values field, that value is array of CustomFieldValue. CustomFieldValue contains current value, custom field description and customized object.
Needed values i reads and alters by sort out. May be it's not best variant, but i acquainted with ruby language not so long ago.
Method for reading custom fields values:
def object_custom_field_value(object, field_name)
object.custom_field_values.each do |field|
if field.custom_field.name == field_name
return field.value
end
end
end
And for changing:
def object_custom_field_set_value(object, field_name, value)
object.custom_field_values.each do |field|
if field.custom_field.name == field_name
field.value = value
end
end
end
Hope this helps!
Get: object.custom_field_value(field.id)
Update: object.custom_field_values = {field.id => val}. Don't forget to save: object.save_custom_field_values. However it doesn't work for creating value, so check via object.custom_value_for(field.id).id?
Create/Update:
Easy. Just add a line object.custom_vield_values before update code. It returns list of values, and triggers creation of empty value. Example
u = User.find(1)
cf = CustomField.where('name', 'mobile phone').first
u.custom_field_values # returns values, triggers creation
u.custom_field_values = {cf.id = "+123456789"} # set value
u.save_custom_field_values # save
Docs: rubydoc
Updating a project's custom field named 'Email':
project = Project.find(1)
cv = CustomValue.where(customized_type: "Project", customized_id: project.id).includes(:custom_field).where(custom_fields: {type: 'ProjectCustomField', name: 'Email'}).first
cv.update value: 'email#mail.com'
I'm building an activerecord to model a conversation tree, using an array column type to represent the materialized path of the record's place in that tree, using postgres 9.1, rails 4.0, and the pg gem.
What I really want to do is access currval('conversations_id_seq') when I create a new conversation object, so that I can pass in [grandparent_id, parent_id ... current_id] as the array to the object initializer. That way I can specify that this column is not null as a database constraint, and in the event of a parentless conversation, have it still default to [current_id].
The problem I have is getting access to the model's id value before I save it the first time. I could always relax the not null constraint and add an after_create hook, but that feels kludgy. I'm hopeful that there's a way I can grab the value that's getting pushed into #id inside the initializer, before the first save to the database.
EDIT to clarify for the bounty: In an ideal world, there would be a special token I could pass in to the object's create method: Conversation.create(reply_chain: [:lastval]), where the gem took that to mean lastval() in the generated SQL.
something like:
def before_create
self.id=Conversation.connection.execute("SELECT nextval('conversations_id_seq')")
self.path = [... , self.id];
true
end
or use a before insert/update trigger to maintain the path.
You could alias the attribute if you don't need the column in the database.
alias_attribute :current_id, :id
Or you could query for the id when you need it.
def self.last_val
ActiveRecord::Base.connection.execute("SELECT lastval('conversations_id_seq')")
end
def self.next_val
ActiveRecord::Base.connection.execute("SELECT nextval('conversations_id_seq')")
end
Conversation.create(reply_chain: Conversation.next_val)
Using after_save isn't the ugliest of code either.
I'm trying to use set_table_name to use one generic model on a couple different tables. However, it seems as though set_table name only works on the class once per application session. For instance in a rails 3 console (ruby 1.8.7) the following happens:
GenericModel.set_table_name "table_a"
puts GenericModel.table_name # prints table_a
pp GenericModel.column_names # prints the columns associated with table_a
GenericModel.set_table_name "table_b"
puts GenericModel.table_name # prints table_b
pp GenericModel.column_names # still prints the columns associated with table_a
Currently the workaround I've found is to also add .from(table_b) so that queries don't error out with 'table_b.id doesn't exist!' because the query still thinks it's FROM table_a.
Can others reproduce the issue? Is this the intended behaviour of set_table_name?
UPDATE
Adding
Model.reset_column_information
after set_table_name forces the model to work as I expect.
Reference found in http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000368
This is probably an undocumented limitation. Once the SHOW FIELDS FROM has been executed, which is where the results from column_names comes from, it is usually cached, at least for the duration of the request. If you must, try using the console reload! method to reset things.
your choice
rename_table
more info at AR TableDefinition
I want to execute one update raw sql like below:
update table set f1=? where f2=? and f3=?
This SQL will be executed by ActiveRecord::Base.connection.execute, but I don't know how to pass the dynamic parameter values into the method.
Could someone give me any help on it?
It doesn't look like the Rails API exposes methods to do this generically. You could try accessing the underlying connection and using it's methods, e.g. for MySQL:
st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?")
st.execute(f1, f2, f3)
st.close
I'm not sure if there are other ramifications to doing this (connections left open, etc). I would trace the Rails code for a normal update to see what it's doing aside from the actual query.
Using prepared queries can save you a small amount of time in the database, but unless you're doing this a million times in a row, you'd probably be better off just building the update with normal Ruby substitution, e.g.
ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")
or using ActiveRecord like the commenters said.
ActiveRecord::Base.connection has a quote method that takes a string value (and optionally the column object). So you can say this:
ActiveRecord::Base.connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{ActiveRecord::Base.connection.quote(baz)}
EOQ
Note if you're in a Rails migration or an ActiveRecord object you can shorten that to:
connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{connection.quote(baz)}
EOQ
UPDATE: As #kolen points out, you should use exec_update instead. This will handle the quoting for you and also avoid leaking memory. The signature works a bit differently though:
connection.exec_update(<<-EOQ, "SQL", [[nil, baz]])
UPDATE foo
SET bar = $1
EOQ
Here the last param is a array of tuples representing bind parameters. In each tuple, the first entry is the column type and the second is the value. You can give nil for the column type and Rails will usually do the right thing though.
There are also exec_query, exec_insert, and exec_delete, depending on what you need.
None of the other answers showed me how to use named parameters, so I ended up combining exec_update with sanitize_sql:
User.connection.exec_update(
User.sanitize_sql(
[
"update users set name = :name where id = :id and name <> :name",
{
id: 123,
name: 'My Name'
}
]
)
)
This works for me on Rails 5, and it executes this SQL:
update users set name = 'My Name' where id = 123 and name <> 'My Name'
You need to use an existing Rails model instead of User if you don't have that.
I wanted to use named parameters to avoid issues with the ordering when I use ? or $1/$2,etc. Positional ordering is kind of frustrating when I have more than a handful of parameters, but named parameters allow me to refactor the SQL command without having to update the parameters.
You should just use something like:
YourModel.update_all(
ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"})
)
That would do the trick. Using the ActiveRecord::Base#send method to invoke the sanitize_sql_for_assignment makes the Ruby (at least the 1.8.7 version) skip the fact that the sanitize_sql_for_assignment is actually a protected method.
Sometime would be better use name of parent class instead name of table:
# Refers to the current class
self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
For example "Person" base class, subclasses (and database tables) "Client" and "Seller"
Instead using:
Client.where(self.class.primary_key => id).update_all(created _at: timestamp)
Seller.where(self.class.primary_key => id).update_all(created _at: timestamp)
You can use object of base class by this way:
person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
Here's a trick I recently worked out for executing raw sql with binds:
binds = SomeRecord.bind(a_string_field: value1, a_date_field: value2) +
SomeOtherRecord.bind(a_numeric_field: value3)
SomeRecord.connection.exec_query <<~SQL, nil, binds
SELECT *
FROM some_records
JOIN some_other_records ON some_other_records.record_id = some_records.id
WHERE some_records.a_string_field = $1
AND some_records.a_date_field < $2
AND some_other_records.a_numeric_field > $3
SQL
where ApplicationRecord defines this:
# Convenient way of building custom sql binds
def self.bind(column_values)
column_values.map do |column_name, value|
[column_for_attribute(column_name), value]
end
end
and that is similar to how AR binds its own queries.
I needed to use raw sql because I failed at getting composite_primary_keys to function with activerecord 2.3.8. So in order to access the sqlserver 2000 table with a composite primary key, raw sql was required.
sql = "update [db].[dbo].[#{Contacts.table_name}] " +
"set [COLUMN] = 0 " +
"where [CLIENT_ID] = '#{contact.CLIENT_ID}' and CONTACT_ID = '#{contact.CONTACT_ID}'"
st = ActiveRecord::Base.connection.raw_connection.prepare(sql)
st.execute
If a better solution is available, please share.
In Rails 3.1, you should use the query interface:
new(attributes)
create(attributes)
create!(attributes)
find(id_or_array)
destroy(id_or_array)
destroy_all
delete(id_or_array)
delete_all
update(ids, updates)
update_all(updates)
exists?
update and update_all are the operation you need.
See details here: http://m.onkey.org/active-record-query-interface