creating the id and timestamp columns with sql? - ruby-on-rails

I'm trying to seed a Rails application with some sql statements in the seed.rb file. There are 12 values supplied, however, the table has 15 columns. The extra three columns are the automatically generated id, created_at and updated at columns that Rails includes by default. If I run a custom sql statement in the seed.rb file in the following manner....
connection = ActiveRecord::Base.connection()
query = "random sql"
connection.execute(query)
Rails doesn't create those columns for me in the way it would if I did
Employee.create!(name: "Joe")
Is there anyway to indicate to rails that I need the id and timestamp columns filled with values when I run an sql statement in seed.rb?

No, because Rails has no way of knowing whether your "random sql" even creates any records for it to fill in ids/timestamps.
When you connection.execute you are on your own, you have forsaken your ORM and given in to the temptation of SQL.
If you can do it using ActiveRecord, then do so! If not, well, that is why Rails lets you drop down to SQL (but think again. Can you really not write it in Ruby?).

Related

Make a new column (migration) between two other columns?

Is there a quick/easy way to make a migration that adds a new column between two existing columns?
Note: I googled and couldn't find an obvious answer. But I also am curious if it's even good practice (since if for some reason other column/s were removed, then the migration may fail?)
If you haven't commited your changes to production you can reorder migrations by rolling them back and then changing the timestamps in the file name.
If thats not an alternative you can actually re-order the columns on some databases directly with SQL even if its not part of the migrations DSL. Migrations are after all really just a DSL to create SQL strings and run them in a repeatable way across environments.
If you can't generate the SQL you want with the DSL you can always use execute to execute a raw DSL string.
# MySQL example
class ReorderYourTableName < ActiveRecord::Migration[5.2]
def change
execute "alter table yourTableName change column yourColumnName yourColumnName dataType after yourSpecificColumnName;"
end
end
However on some DBs you can't actually reorder the columns without extensive steps of creating new columns and shuffling the data around.

How to create postgres database for Rails app manually?

How can I create postgres db for Rails app properly but in psql, not via rake db:create?
I mean, one can always write CREATE DATABASE project_name, but I don't know what happens in that rake task under the hood. Maybe there are a lot of additional params.
Update
After first answer I decided to clarify: I know how to write and use migrations, they are awesome, but my question not about them. It's about rake db:create task and pg adapter.
In other words, I just want to know which command in psql is equal to rake db:create.
If you select the db on pgadmin III it will show you the sql instructions with the local things to load. They are very importanst if you have full text index on. You must run them from the database postgres.
Rails expects table names to match model names but be plural and snake_case. For example, a User model will store records in a users table and a BlogEntry model will store records in a blog_entries table.
Rails expects a table's primary key to be named id and it expects foreign keys to match model names but be snake_case and end with _id. For example, if BlogEntry belongs_to User, Rails will expect the blog_entries table to have a user_id column.
Join tables (such as used with many-to-many relations) are expected to be named with the two models' names in plural snake case and alphabetical order. For example, a join table describing a many-to-many relation between a User model and a Blog model would be expected to have the name blogs_users and have, at the least, the columns blog_id and user_id.
Those are the basics. Of course, all of this is configurable: For example, you can use the table_name class method to tell a model to use a table with a different name, and the relation methods (belongs_to, has_many, etc.) all take options letting you specify different names.
Apart from these naming conventions Rails doesn't require anything special from a database, as long as the correct credentials and configuration are specified in config/database.yml.

Can activerecord put user-defned sql in a from clause?

I am attempting to build a "pull some data out and display it" app. To pull the data out, we have a great deal of site-specific oracle sql (stored procedures, weird expressions and whatnot).
One way of doing what I need to do is to create oracle views and then to point activerecord at those views. But I would rather have all the SQL in one place - the ruby app itself - rather than in oracle and ruby. I'd like to make an active record object over a raw SQL select clause, and then be able to use all the usual activerecord stuff with this.
Now - I know Oracle SQL supports this. Let's take:
select n.name_id, name, munge(n.name) as munged from name
Normally you'd create a view munged_name, a class MungedName, with the result that
MungedName.like(:name , 'Foo%')
would (eventually) generate SQL that looks like this:
select T1.name_id, T1.name, T1.munged
from munged_name as T1
where T1.name like 'Foo%'
What I would like to do, however, is have ActiveRecord use the sql select as a subquery in the FROM clause:
select T1.name_id, T1.name, T1.munged
from (select n.name_id, name, munge(n.name) as munged from name) as T1
where T1.name like 'Foo%'
Just jam it in there literally - Oracle will optimise it perfectly ok. This way, all the weird queries that my people want to do are in the one spot - in the model class definition.
Will ActiveRecord do this? I'll keep looking - see if I can find out before I get an answer here :) .
I suspect that passing that SQL fragment should work.
MungedName.from("select n.name_id, name, munge(n.name) as munged from name").like(:name , 'Foo%')
But you're probably looking for something even more Ruby-like, so you can of course generate that from fragment in Arel too:
sql = Name.select([:name_id, :name, "munge(name)"]).to_sql
MungedName.from(sql).like(:name , 'Foo%')
I did not try any of this from the command line. I have doubts that what I entered will work cleanly, but the idea is basically the same. Arel can be used to generate complex SQL, and have that SQL be fed into another Arel query.
I don't know for certain about the from part, but I suspect that will work as expected. If you check out the method source:
http://www.ruby-doc.org/gems/docs/a/arel-3.0.2/Arel/SelectManager.html#method-i-from
def from table
table = Nodes::SqlLiteral.new(table) if String === table
...
end
The SqlLiteral class is just a way to bypass any quoting or whatnot.
Looking at the code - it won't work. There is no self.from= method. When you jam raw sql in the table_name, it quotes that and treats it as though it were a table name. Which is fair enough. The activerecord ruby has a thing 'quoted_table_name' all the way through it.
There is, however, an intriguing comment on associations.rb:
# == Table Aliasing
#
# Active Record uses table aliasing in the case that a table is referenced multiple times
# in a join. If a table is referenced only once, the standard table name is used. The
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
# Indexes are appended for any more successive uses of the table name.
If activerecord could be persuaded to always alias the MungedName model (even when there was no join), and to use some unquoted thingy as the content of the from, that might work. I'll have to hack up the library, though.

Can't insert row in table with computed column?

I've got a Rails 3 applicaiton running on SQL Server against a legacy database with some computed columns. When I try to save a new record, I get an error "The column "ContractPendingDays" cannot be modified because it is either a computed column or is the result of a UNION operator
I'm not updating this column, it just seems to be one of the columns activerecord tries to write to when adding a new record into the db.
Is there some way to stop this behavior? I even tried changing schema rb but that didn't help. (and suboptimal anyway since I'd have to do it every time I change the db.)
Is there some way to mark a column as not updatable so activerecord doesn't try to write to it?
Found answer here:
Prevent NULL for active record INSERT?
(Use attributes.delete in a before_create filter)

new records insertion into database table in my case

I am developing a Rails v2.3 application which is a service to search projects' information where projects info are stored in database.
There is an existing projects table in the database like following:
For the sake of satisfying customer’s requirement, this table needs to insert new data in the mid-night everyday.
The reason of creating these new records is to make the Rails application be able to search projects by a single word besides searching by the full name.
For example, if search by word "portal", both Car rental portal and Position track portal records should be found by the Rails application. That's the app.'s database needs to have all the records of each single word from project_name.
So, my plan is to generate those new records by spliting the value in project_name column (of the above projects table) into single words and then use each single word as a new record's project_name while keep other columns of the record unchanged.
For example, in above table, the first record has project_name "Car rental portal", what I gonna do is to split this string into 3 words and construct the following three new records to be inserted into the table:
To achieve this. I tried to make a rake task which gets all records from the original projects table, and for each record, the rake task splits the string value of project_name column into words, then construct the new records with words and insert into the table. My rake task looks like the code below:
all_records = ActiveRecord::Base.execute("select * from projects;")
all_records.each do |record|
user_id = record[0]
project_name=record[1]
department = record[2]
other = record[3]
words=project_name.split()
words.each do |word|
sql = "insert into project values (#{user_id},#{word},#{department},#{other});"
ActiveRecord::Base.execute(sql)
end
end
The rake task works well, it creates the expected new records and inserted into the projects table, BUT the problem is it takes 36 hours to complete!
It is understandable since the origin table is very very large, if split the string to words and create the new record it's like create a 3 times larger table (suppose each string of project_name has 3 words).
My question:
Could some Rails experts suggest me some more efficient way to achieve the new record insertion thing I described above?
Or any new way to enable single word search in my case? (That's do not use the way I designed to have each single word store in the database.)
If you do this only for searching purpose, why don't you use Sunspot ? It supports full text search.
Splitting project name sounds like a really bad idea for me.
But if you want to take it less time, then I'd encourage you to split this single task into more rake tasks, that would do the same, but for other set of projects.
For faster importing, you want to use activerecord-import, it will speed up your execution by a couple orders of magnitude.
columns = [:title, :project_name, :department, :other]
values = all_records.inject([]) do |values_arr, record|
user_id, project_name, department, other = record
project_name.split.each do |name|
values_arr << [user_id, name, department, other]
end
values_arr
end
class TempModel < ActiveRecord::Base; set_table_name "projects"; end
TempModel.import columns, values, :validate=>false

Resources