Using ActiveRecord with column names with spaces in them - ruby

I work on a team that is using ActiveRecord to access the schema on a MSSQL server. Modifying the schema is not an option and the column names have spaces in them, a la SPACEY COLUMN.
When writing the ActiveRecord class to access a table with spaces in it, what is good practice?
We also need this to work with factory girl...

AR-JDBC (as well as the AR-SQLServer-Adapter) will/should handle this just fine since it auto-magically quotes column name identifiers using "[ COlumn NAME ]" ... I personally would hide this from bubbling up as much as possible e.g. using aliases :
class MySpacey < ActiveRecord::Base
set_table_name 'SPACEY TABLE'
set_primary_key 'MY ID'
alias_attribute :id, :'MY ID'
end

Consider User is the Model and User Name is the column you need to access.
User.where('User Name' => 'Bob')
You can add multiple conditions also,
User.where('User Name' => 'Bob', 'Email Address' => 'sample#sample.com')
You can also try,
User.where('[User name] = ? AND [Email Address] = ?', 'Bob', 'sample#sample.com')
And if your table itself has space in it. Try,
class User < ActiveRecord::Base
self.table_name = "user table"
end

Related

Insert violates foreign key constraints

My models:
class Team < ApplicationRecord
belongs_to :objective
belongs_to :consultancy
has_one :seminar, :through => :consultancy
belongs_to :consultant, class_name: "Student"
has_and_belongs_to_many :users
end
class User < ApplicationRecord
has_and_belongs_to_many :teams
end
My function:
def find_placement(student)
team = #consultancy.teams.detect{|x| x.has_room && student.objective_students.find_by(:objective => x.objective).obj_ready_and_willing?}
team.users << student if team && team.valid? && team.persisted?
return team
end
And this is the error I'm currently receiving:
PG::ForeignKeyViolation: ERROR: insert or update on table "teams_users" violates foreign key constraint "fk_rails_8299e376e9" DETAIL: Key (team_id)=(3396) is not present in table "teams". : INSERT INTO "teams_users" ("user_id", "team_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4)
This error only occurs in production environment, not development or testing.
I've tried using console mode to manually run each line of the function. No error is triggered.
From this question https://dba.stackexchange.com/questions/29147/postgresql-insert-update-violates-foreign-key-constraints, I found this explanation.
"If a new row is inserted, check DataID and Address: if they contain a non-NULL value (say 27856), then check Table1 for DataID˙and Table2 for Address. If there is no such value in those tables, then return an error."
If I understand this paragraph correctly, my function is trying to insert a student into a team that doesn't exist. But doesn't the "if team && team.valid? && team.persisted?" part of my code take care of that? If not, what is a better way to insert the student into a team, but only if the team already exists.
Thank you in advance for any insight.
The valid? method does not check database persistence, so you may or maynot need it. You'll probably want to insure the team model is persisted and not deleted from the teams table. To be safe, you might check both. ActiveRecord#persisted? method:
def find_placement(student)
if team = #consultancy.teams.detect{|x| x.has_room && student.objective_students.find_by(:objective => x.objective).obj_ready_and_willing?}
team.users << student if team.valid? && team.persisted?
end
team
end
The key to getting past this was to reload.
Before, #consultancy was collecting all of the Teams, including some that had been destroyed.
#consultancy.reload.teams
only collects the teams that still exist.
I would not have come to this conclusion without the comment from #SergioTulentsev and the answer from #lacostenycoder. Thanks!

Mongoid push with upsert

I've got model User:
class User
field :username, type: String
embeds_many :products
end
class Product
field :name, type: String
embedded_in :user
end
I would like to have single operation that would:
insert the user
update the user in case the user exists already (this i can easily do with upsert)
push the products
This works for upserting:
User.new(username: 'Hello').upsert
The problem is that this will delete the embedded products (the products attribute is not specified).
Can I ask mongoid to skip setting array to empty?
Can I ask mongoid to push new products at the end of products array?
Something like this:
User.new(username: 'Hello').push(products: [Product.new(name: 'Screen')]).upsert
Finally I ended up by manually writing the following query:
User.mongo_client[:users].update_one({username: 'Hello'},
{"$set" => {first_name: 'Jim', last_name: 'Jones'},
"$pushAll" => [products: [{name: 'Screen'}, {name: 'Keyboard'}]
},
upsert: true)
Where:
$set - are the params that we want to set for a given document
$pushAll - when you use $push you can specify only one element, $pushAll allows you to append multiple elements (when you specify only one it will behave like $push)
upsert - will do the insert/update magic in the mongodb
In the second hash you can also specify $inc, $dec, $pop, $set etc... which is quite useful.

How to disable sequence in activerecord-oracle_enhanced-adapter without rails?

I am only using ActiveRecord to some development with a legacy Oracle database. My adapter is activerecord-oracle_enhanced-adapter (https://github.com/rsim/oracle-enhanced). So I don't want to AR handle the primary key generation. How to disable the primary key generated by a sequence?
class User < Activied::Base
self.table_name = "users"
self.primary_key = "user_id"
end
user = User.new
user.save
Then I got the error:
stmt.c:230:in oci8lib_191.so: ORA-02289: sequence does not exist (OCIError)
When I change my code to
class User < ActiveRecord::Base
self.table_name = 'users'
self.primary_key = "user_id"
self.sequence_name = nil
end
I got another error:
stmt.c:230:in oci8lib_191.so: ORA-00936: missing expression (OCIError)
So is there anyone know how to manage the primary key manually? I just want to do some simple insert.
Thanks
Maybe it's too late but finaly I found answer. Shortly:
self.sequence_name = :autogenerated
From the source code comment here:
Usage notes:
# * Key generation assumes a "${table_name}_seq" sequence is available
# for all tables; the sequence name can be changed using
# ActiveRecord::Base.set_sequence_name. When using Migrations, these
# sequences are created automatically.
# ***Use set_sequence_name :autogenerated **** with legacy tables that have
# triggers that populate primary keys automatically.**

ActiveRecord, find by polymorphic attribute

Having this:
class Event < ActiveRecord::Base
belongs_to :historizable, :polymorphic => true
end
user = User.create!
I can:
Event.create!(:historizable => user)
But I can't:
Event.where(:historizable => user)
# Mysql2::Error: Unknown column 'events.historizable' in 'where clause'
I have to do this instead:
Event.where(:historizable_id => user.id, :historizable_type => user.class.name)
Update
Code that reproduces the issue: https://gist.github.com/fguillen/4732177#file-polymorphic_where_test-rb
This has been implemented in Rails master and will be available in
Rails 4. Thanks.
– #carlosantoniodasilva
I do this:
user.events
This is a proper AR query, you can chain it with other scopes and stuff:
user.events.where(<your event conditions here>)
EDIT: AFAIK the other way around you must specify both fields (makes sense: you could have a user with id 4 and another thing with events, like a Party, also with id 4).
EDIT2: Regarding "Why does create work and where doesn't": create is more highlevel than where, because the former deals with "a complete model", while the latter manages things at the database table level.
ActiveRecord's create (AFAIK) uses a combination of new + update_param somewhere down the line.
update_param uses your model's xxxx= methods for assigning each individual property.
In your example, historizable= is a method built by the belongs_to expression. Since the belongs_to "knows" that it's polymorphic, it can deal with the type and id.
On the other hand, when you pass a hash to the where clause, the parameters there only refer to database fields. Rails provides scopes for "higher level" access:
class Event < ActiveRecord::Base
...
scope :by_historizable, lambda { |h| where(:historizable_id => h.id, :historizable_type => h.class.name) }
end
...
Event.by_historizable(user).where(<your other queries here>)
I've heard that this might change in Rails 4, and where might be more "intelligent". But I have not checked yet.
Try:
Event.joins(:historizable).where(:historizable => {:historizable_type => user})

ROR- Cannot use Find in a 1-many relationship

In Ruby on rails, our model includes orders and payments.
There's 1-many relationship between order and payments.
In the orders model we specify:
has_many :payments, :as => :payable
And a payment record has payable_id that is set to order.id.
In a report, I want to select all payments that belong to orders of a given type.
Using:
payments = Payment.find(:all, :conditions => conditions)
and adding 'payable.type="a" ' to the conditions doesn't work.
It seems that ActiveRecord doesn't develop this into a correct join statement (payable_id=order.id and orders.type='a').
I cannot use explicit SQL here, as the condition contains other things that are inserted there earlier in the code.
Thanks,
Raffi Lipkin
Your conditions clause is wrong.
You state that an Order
has_many :payments, :as => :payable
This tells me that a Payment
belongs_to :payable, :polymorphic => true
This means that the payments table has two columns of note: payable_id and payable_type. This also means that Payments can be applied not just to Orders, but also to other models as well (CreditCardBalances, who knows).
If you want to query for payments of a specific type, i.e. belonging to any instance of a particular class, you need to be querying the field payments.payable_type. This works fine:
Payment.find(:all, :conditions => "payable_type = 'Order'")
Here's a gist that shows what I did to test this. The models created are set up just like described above.
Don't forget that you can extract that into named scopes if it's easier:
named_scope :on_orders, :conditions => "payable_type = 'Order'"
Which makes it
Payment.on_orders
Or dynamically:
named_scope :on, lambda { |type| { :conditions => "payable_type = '#{type.to_s}'" } }
Which then makes it
Payment.on(Order) # or Payment.on(CreditCardBalance) or Payment.on("Order")
Try incliding and reference the actual table id name in the condition, rather than the association alias:
find(:include => "payments", :conditions => ["payment.type = ?", "x"]
You mention 'payment type'. If they're fairly static, have you considered using single table inheritance (STI) to subclass your different payment types? Then Rails will do all the magic to filter on type.
E.g.
class CreditCardPayment < Payment
...
end
It doesn't even need to exhibit different behaviour initially; however you'll probably find that it turns out to be really useful to have different data and polymorphic behaviour around payments.

Resources