Update hash with Rails 4 - ruby

I have an hash object:
#chosen_opportunity = {"id"=>66480, "prize_id"=>4, "admin_user_id"=>1, "created_at"=>2015-09-20 18:37:29 +0200, "updated_at"=>2015-09-20 18:37:29 +0200, "opportunity_available"=>true}
How do I update the value of deal_available to false?
I tried this but it fails:
#chosen_opportunity['deal_available'] = false
#chosen_opportunity.save
controllers/deal_controller.rb:
def show_opportunity
#deal = Deal.friendly.find(params[:id])
#chosen_opportunity = Opoortunity.find_by_sql(
" SELECT \"opportunities\".*
FROM \"opportunities\"
WHERE (deal_id = #{#deal.id}
AND opportunity_available = true)
ORDER BY \"opportunities\".\"id\" ASC LIMIT 1"
)
# comes from http://apidock.com/rails/ActiveRecord/Base/find_by_sql/class
#chosen_opportunity[0].attributes['opportunity_available'] = false
#chosen_opportunity[0].save
respond_to do |format|
format.js
end
end
Can I update the value of opportunity_available from the Opportunity model inside a Deal controller? Is that why it's not working?
I know I could use Active Record but I need to use raw PostgreSQL for the first query. Thanks for your understanding of this non very Rails-y way.

You can change your code to:
#chosen_opportunity = Opportunity.find_by deal_id: deal.id, opportunity_available: true
#chosen_opportunity.opportunity_available = false
#chosen_opportunity.save!
This is much more Rails compatible. In addition, if I'm not mistaken, Rails won't let you save an object that you got through find_by_sql, so at the least you'd need to get a proper model object from the result. You can write (very ugly) code like:
Opportunity.where(id: #chosen_opportunity[0].attributes['id'])
.update_all(opportunity_available: false)
Warning: This will update the database, but not the #chosen_opportunity[0] object.

There is no method save on a hash. You have to do it on the model that holds that hash as an instance variable.

Related

Reduce SQL Queries from Includes assoc

I try to reduce SQL queries from my Rails application.
I have some controller like:
class Rest::MyController < Rest::BaseController
def show
render xml: some_model, status: :ok
end
private
def my_associations
[
:model2,
:model3,
:model4,
]
end
def some_model
#some_model ||= SomeModel.includes(my_associations).where(id: test_params[:id])
end
def test_params
params.permit(:id)
end
end
To avoid N + 1 I use includes, so basically when i try to execute some_model method, AR make lot of call's like that (SELECT ALL FROM):
SomeModel Load (1.7ms) SELECT `model2`.* FROM `model2` WHERE `model2`.`type` IN ('SomeModel') AND `model2`.`is_something` = 0 AND `model2`.`id` = 1
SomeModel Load (1.7ms) SELECT `model3`.* FROM `model3` WHERE `model3`.`type` IN ('SomeModel') AND `model3`.`is_something` = 0 AND `model3`.`id` = 1
SomeModel Load (1.7ms) SELECT `model4`.* FROM `model4` WHERE `model4`.`type` IN ('SomeModel') AND `model4`.`is_something` = 0 AND `model4`.`id` = 1
This is only example
Now, through my serializer I would like to get only selected columns for model2, model3 and model4
Unfortunately Active record make a call like SELECT model2.* FROM
For example, for model2 serializer i try to get only (:id, :name) columns.
Is it possible to make a call like ?
SELECT some_model.*, model2.id, model2.name FROM `some_model`
instead
SELECT `model2`.* FROM `model2` WHERE `model2`.`type` IN ('SomeModel')
If you want to use Rails's includes feature, then no, there isn't an especially good way to selectively control the columns from included models.
If you're looking for help to optimize the query, you'll need to provide more specifics about the data schema and the desired output.

How can I create a complete_name field in a custom module for a custom hierarchy like used on product categories in Odoo?

I'm trying to create a field “complete_name” that displays a hierarchy name similar to whats done on the product categories grid but I can't seem to get it to work. It just puts Odoo in an endless loading screen when I access the relevant view using the new field "complete_name".
I have tried to copy the code used in addons/product/product.py and migrate to work with Odoo 9 API by using compute instead of .function type but it did not work.
Can someone help me understand whats wrong? Below is my model class which works fine without the complete_name field in my view.
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )
complete_name = newFields.Char( compute = '_name_get_fnc', string = 'Name' )
def _name_get_fnc( self ):
res = self.name_get( self )
return dict( res )
Your compute function is supposed to define the value of an attribute of your class, not return a value. Ensure the value you are assigning complete_name is a string.
Also name_get() returns a tuple. I am not sure if you really want a string representation of this tuple or just the actual name value.
Try this
def _name_get_fnc( self ):
self.complete_name = self.name_get()[1]
If you really want what is returned by name_get() then try this.
def _name_get_fnc( self ):
self.complete_name = str(self.name_get())
If you are still having issues I would incorporate some logging to get a better idea of what you are setting the value of complete_name to.
import logging
_logger = logging.getLogger(__name__)
def _name_get_fnc( self ):
_logger.info("COMPUTING COMPLETE NAME")
_logger.info("COMPLETE NAME: " + str(self.name_get()))
self.complete_name = self.name_get()
If this does not make it apparent what the issue is you could always try statically assigning it a value in the off chance that there is a problem with your view.
def _name_get_fnc( self ):
self.complete_name = "TEST COMPLETE NAME"
After further review I think I have the answer to my own question. It turns out as with a lot of things its very simple.
Simply use "_inherit" and inherit the product.category
model. This gives access to all the functions and fields
of product.category including the complete_name field
and computes the name from my custom model data. I was
able to remove my _name_get_func and just use the inherited
function.
The final model definition is below. Once this
update was complete I was able to add a "complete_name" field
to my view and the results were as desired!
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_inherit = 'product.category'
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )

How to update multiple columns in Ruby on Rails 3?

Just out of curiosity, is there a way to say this...
user.update_column(:field1, true)
user.update_column(:field2, true)
... in one line in Ruby on Rails?
As far as I know an update_columns method does not exist...
You can use update_all as follows:
User.where(:id => user.id).update_all({:field1 => true, :field2 => true})
This will generate the following update statement (mysql):
UPDATE users SET field1 = 1, field2 = 1 WHERE users.id = <whatever>
Callbacks and validations will not be run.
Note: this is only available in ActiveRecord v4.0.2 and higher:
You can do in this way:
update_columns(field1: value, filed2: value)
what about doing it like this:
user.attributes = attributes
user.save(validate: false)
If you need speed you can also call execute directly on AR connection. I used something like this to import large amount of data.
connection = ActiveRecord::Base.connection
email = connection.quote(email)
zip = connection.quote(zip)
connection.execute(%{UPDATE "users" SET "email" = #{email}, "zip" = #{zip} WHERE "users"."id" = #{user.id}})
Note that validations and callbacks will not be run.
Copied from https://stackoverflow.com/a/25171127/1520775

Use default value if item not found in array

So I've got my code trying to select an object from an array of objects, and if the object isn't found, I want to create my defaults.
lead_time = lead_times.select{|d| LeadTimeProfile.new unless d.day_of_week == day }
however, from what I can tell, this is not returning me the devault LeadTimeProfile.
is there a way of doing this? Or have I got it right?
So I've got my code trying to select an object from an array of objects, and if the object isn't found, I want to create my defaults.
Take a look at Enumerable#find
lead_time = lead_times.find{ |d| d.day_of_week == day } || LeadTimeProfile.new
filter your array first, and then do the construction
lead_time = lead_times.select{|d| d.day_of_week == day}.map {|d| LeadTimeProfile.new(d)}
Passing a lambda as a parameter also works.
lead_time = lead_times.find(lambda { LeadTimeProfile.new } ){ |d| d.day_of_week == day }
Here is another way to get the same results as what Kyle posted. There is no difference between this and using a or gate other than maybe making chaining method calls a bit cleaner.
day = 2
lead_times.find(-> { LeadTimeProfile.new }) { |p|
p.day_of_week == day
}.day_of_week

get last insert id when using Activerecord

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.

Resources