Rails sequence column on Model - ruby-on-rails

I followed this mini-tutorial successfully to have a column (car_code) in my table (cars) to work like a sequence (using postgreSQL database).
As Result I have this table:
CREATE TABLE Cars
(
id serial NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
car_date timestamp without time zone,
car_code integer DEFAULT nextval('car_code_sequence'::regclass),
CONSTRAINT cars_pkey PRIMARY KEY (id )
)
My insert statement works fine:
INSERT INTO cars(created_at, updated_at, car_date) VALUES (now(), now(), now());
--1|Date|Date|Date|2 <--using my car_code_sequence
Unfortunately, when i invoke a "create" operation in a "car_controller the rails application generate this statement:
INSERT INTO "cars" ("created_at", "car_code", "car_date", "updated_at")
VALUES ($1, $2, $3, $4) RETURNING "id" [
["created_at", Mon, 04 Mar 2013 14:39:55 UTC +00:00],
["car_code", nil],
["car_date", Mon, 04 Mar 2013 14:39:55 UTC +00:00],
["updated_at", Mon, 04 Mar 2013 14:39:55 UTC +00:00]]
So, my question is:
Who can I change the Car Model to exclude the column "car_code" from insert statement (but keeping the "car_code" in database), i.e., to have the same behaviour as "id").

Try to add this code at car model:
class Car < ActiveRecord::Base
## --------------------- Ignore columns patch ------
##ignore_column_pattern = /^car_code/
class << self
alias :all_columns :columns
def columns
#columns_filt ||= all_columns.reject { |col| col.name =~ ##ignore_column_pattern }
end
end
alias :all_attribute_names :attribute_names
def attribute_names
#attr_names_filt ||= all_attribute_names.reject { |att| att =~ ##ignore_column_pattern }
end
## ------------------- / Ignore columns patch ------
end
From: https://stackoverflow.com/a/10319903/1042324

Related

Return virtual Postgres column after create

I have a Rails 7 model that uses Postgres' virtual column feature:
create_table :time_entries do |t|
# ...
t.virtual :duration, type: :interval, as: %(("to" - "from")::interval), stored: true, null: false
# ...
end
The problem is, that after I create a record via Rails create(...) these virtual column is nil:
[16] pry(main)> TimeEntry.create(from: Time.zone.now, to: 1.day.from_now)
TRANSACTION (0.3ms) BEGIN
TimeEntry Create (0.5ms) INSERT INTO "time_entries" ("from", "to", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"
[["from", "2022-11-18 06:45:11.419000"], ["to", "2022-11-19 06:45:11.420000"], ["created_at", "2022-11-18 06:45:11.420862"], ["updated_at", "2022-11-18 06:45:11.420862"]]
TRANSACTION (0.9ms) COMMIT
=> #<TimeEntry:0x0000ffff86f1ae10
id: 13,
from: Fri, 18 Nov 2022 06:45:11.419000000 UTC +00:00,
to: Sat, 19 Nov 2022 06:45:11.420000000 UTC +00:00,
duration: nil,
created_at: Fri, 18 Nov 2022 06:45:11.420862000 UTC +00:00,
updated_at: Fri, 18 Nov 2022 06:45:11.420862000 UTC +00:00>
When you reload the model, duration is set.
I found out, that this is due to Rails only returning the id column using RETURNING "id" at the end of the INSERT INTO statement. When you execute the query in Postgres directly you can return the generated duration column directly after the insert:
app_development=# INSERT INTO "time_entries" ("from", "to", "created_at", "updated_at") VALUES ( '2022-11-18 06:34:46.889000', '2022-11-18 06:34:56.889000', '2022-11-18 06:34:46.889000', '2022-11-18 06:34:46.889000') RETURNING "duration", "id";
duration | id
----------+----
00:00:10 | 11
(1 row)
Is it possible to customize the RETURNING "id" part of the SQL query in my model, so that the instance of TimeEntry already have the duration set after I create it?
EDIT:
I found the code segment inside the Postgres Adapter and tried to monkey patch it like this:
require "active_record/connection_adapters/postgresql/database_statements"
module PostgresReturningPatch
def sql_for_insert(...)
sql, *args = super
if sql.include?(TimeEntry.table_name) && sql.ends_with?(%(RETURNING "id"))
returning_virtual_columns = TimeEntry::columns.select(&:virtual?).map do |column|
quote_column_name(column.name)
end.join(", ")
sql += ", #{returning_virtual_columns}"
end
binding.pry
[sql, *args]
end
end
ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements.module_eval do
prepend PostgresReturningPatch
end
Unfortunately, it's still nil when Rails returns the instance of my model, despite that the SQL ends now with RETURNING "id", "date", "duration".

Build vs Create in has many through relationship

I wonder if anyone can help me to understand about the difference between build and create in Has Many Through (HMT) relationship?
From my understanding after reading through stackoverflow and google, create is essentially build + save. However, it seems like create does more than just build + save. It also somehow save into the join table of the HMT relationship as shown below.
I created 3 models: user, wiki, and collaborator.
class User < ActiveRecord::Base
has_many :wikis, through: :collaborators
has_many :collaborators
end
class Wiki < ActiveRecord::Base
has_many :users, through: :collaborators
has_many :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :wiki
end
Then I tested the build and create behavior in rails console:
$rails c
Loading development environment (Rails 4.0.10)
> user = User.first #create instance user
User Load SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, name: "Jayzz55">
> (user.wikis.build(title:"hello",body:"world")).save #build and save wiki
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES(?,?,?,?) [["body","world"], ["ccreated_at, Sat, 08 Nov 2014 01:39:36 UTC +00:00], ["title","hello"], ["updated_at",Sat, 08 Nov 2014 01:39:36 UTC +00:00]]
=> true
>wiki1 = Wiki.first #create instance wiki called wiki1
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis.count
=> 0
>wiki1.users
=> []
>Collaborator.all
=> []
> user.wikis.create(title:"second title",body:"another one")
begin transaction
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)[0m [["body", "another one"], ["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["title", "second title"], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00]]
SQL INSERT INTO "collaborators" ("created_at", "updated_at", "user_id", "wiki_id") VALUES (?, ?, ?, ?) [["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["user_id", 1], ["wiki_id", 2]]
=> #<Wiki id:2, title:"second title",body:"another one>
>user.wikis
=> [#<Wiki id: 1, title:"hello",body:"world">, #<Wiki id:2, title:"second title",body:"another one>]
>user.wikis.count
=> 1
>wiki2.users
=>#<User id: 1, name: "Jayzz55">
>Collaborator.all
Collaborator Load SELECT "collaborators".* FROM "collaborators"
=> #<Collaborator id:`, user_id:1, wiki_id: 2>
Case wiki1 : build and then save it
The data is not saved into the join table. calling user.wikis does return the object wiki, but running user.wikis.count return 0. Furthermore, running wiki1.users doesn't return the object user. Checking the join table Collaborator returns empty array.
Case wiki2 : create
The data is saved into the join table. calling user.wikis does return the object wiki. Running user.wikis.count return 1. Furthermore, running wiki1.users does return the object user. Checking the join table Collaborator, it does show the relationship clearly mapped.
Seems like Create is not simply just build + new. I'm curious about this behavior and hopefully someone can share their knowledge on this.
I believe that if you had in your first case instead written:
user.wikis.build(title:"hello",body:"world")
user.save
... you would find that ActiveRecord does the "full" save that create also does. The way you've written it, you're asking ActiveRecord to save the newly created Wiki instance, but not the association. So it doesn't create the record in the join table that's required.
Build + Save is equivalent to Create in any relationship

has_many through middle model not creating, but creating duplicate of one model

I have two models join by a middle model
class Integration < ActiveRecord::Base
has_many :integration_records
has_many :records, through: :integration_records
end
class IntegrationRecord < ActiveRecord::Base
belongs_to :integration
belongs_to :record
end
class Record < ActiveRecord::Base
has_many :integration_records
has_many :integrations, through: :integration_records
end
i = Integration.create(whatever)
i.records.create(whatever)
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
i.records
=> [one record]
Record.all.count
=> 2
i.integration_records
=> #<IntegrationRecord id: nil, integration_id: 1, record_id: 2 >
Notice id is nil
IntegrationRecord.all
=> #<ActiveRecord::Relation []>
IntegrationRecord.create
=> #<IntegrationRecord id: nil, integration_id: nil, record_id: nil>
Notice id is nil
Record.create.integrations.create
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "integrations" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
Not sure why this is happening, in the case of i.records.create(whatever) it should output:
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
INSERT INTO "integration_records" ("created_at", "data", "integration_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 03 May 2014 15:57:05 UTC +00:00], ["data", {}], ["integration_id", 5], ["updated_at", Sat, 03 May 2014 15:57:05 UTC +00:00]]
I should note that for some reason, when I create a new app in rails 4.0.4, I still had this problem. But when I change the names of the models and specifically Record model, it works perfectly fine. So when I changed it to Recordrow no problem at all.
As per your current association setup, all you need to do is, just follow these simple instructions step by step
Create an Integration record
## this will create an "integration" record in "integrations" table
integration = Integration.create(whatever)
Create an associated Record record
## this will create an associated "record" entry in "records" table
## PLUS this will also created an associated record in "integration_records" table
integration.records.create(whatever)
View the associated records and associated integration_records
## Display all the "records" entries associated to this particular integration
integration.records
## Display all the "integration_records" entries associated to this particular integration
integration.integration_records
UPDATE
i.records.create(whatever) was creating 2 records, found out that the issue was with the name records of the table. Once changed everything works fine. It looks like records is reserved word.
Also, OP found this link which states records is reserved

Ruby on Rails Database Niling

So, I have my database, but whenever I call Model.create(:thing => "Hi") it just "does it". When I look at it, my records are All nils! (Minus the ID and Timestamp, those are managed by active record.) Is it the way I create them or IS it my model??? I am using Rails 4.0.1, and it's corresponding active record version. So, what is it? What problems could create this?
My logs:irb(main):003:0>
Email.create(:user => User.find(3), :email => "HIDDEN#HIDDEN.HIDDEN", :key => Email.gen("gemist", "HIDDEN#HIDDEN.HIDDEN"))
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
WARNING: Can't mass-assign protected attributes for Email: user, email, key
(0.1ms) begin transaction
SQL (2.9ms) INSERT INTO "emails" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00], ["updated_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00]]
(1.0ms) commit transaction
=> #<Email id: 3, email: nil, User_id: nil, key: nil, confirmed: nil, created_at: "2014-01-01 21:22:24", updated_at: "2014-01-01 21:22:24">
And my model, incase you are wonder about ZE Generate function.... or anything else
class Email < ActiveRecord::Base
belongs_to :User
def self.gen(user,email)
# Make conf keys on demand
# Salting is used for randominzg and uniquisng, in the case we have already
# sent keys to the same email (We don't want the samekeys more than once!)
# Comboing to make sure that incase of two users who want to confirm the
# same email
salt = BCrypt::Engine.generate_salt
combo = user + email
return BCrypt::Engine.hash_secret(combo, salt)
end
end
I can offer a Migration or schmea if needed.
Now I know under "How to edit" I need to respect the original author, but I have no self respect.
Your question contains the answer already:
WARNING: Can't mass-assign protected attributes for Email: user, email, key
and:
INSERT INTO "emails" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00], ["updated_at", Wed, 01 Jan 2014 21:22:24 UTC +00:00]]
You can't set user, email, and key during a mass-assignment (which create() is doing), so it's ignoring those. The only fields being set, as you can see from the INSERT log, are the timestamp fields. So you end up with a fairly empty record in the database.
You can set those fields individually on a model instance, or you can flag them as mass-assignable by putting this in your model:
attr_accessible :user, :email, :key
You may very well want to leave them protected, though. Here's an article on mass-assignment protection. If you're processing form data, you probably want to leave the protection in place for certain fields. If your create() is already using trusted data, you can make them accessible.

Rails Postgresql Time Column

I've got 2 Time Without Time Zone columns in my table (start_time and end_time). When an event occurs, I need to see which of these columns has a start_time < Time.now < end_time. The problem seems to be that Rails is not saving the Time in UTC and is instead saving the time exactly as I enter it in my field. My local timezone is UTC -5, so if I put in an end_time of 09:00:00, I would expect that to get saved to the database as 14:00:00, but it's getting saved as 09:00:00, so when I later run my start_time < Time.now < end_time query, I'm getting the wrong results (I'm off by those 5 hours).
Here's an example (the model is called TimeParameter):
1.9.3-p392 :001 > TimeParameter.create(start_time: "00:30:00", end_time: "09:00:00")
(7.0ms) SELECT * FROM geometry_columns WHERE f_table_name='time_parameters'
(0.2ms) BEGIN
SQL (9.8ms) INSERT INTO "time_parameters" ("created_at", "day_of_week", "end_time", "start_time", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Tue, 23 Jul 2013 14:29:06 UTC +00:00], ["day_of_week", nil], ["end_time", 2000-01-01 09:00:00 UTC], ["start_time", 2000-01-01 00:30:00 UTC], ["updated_at", Tue, 23 Jul 2013 14:29:06 UTC +00:00]]
(308.9ms) COMMIT
=> #<TimeParameter id: 24, start_time: "2000-01-01 00:30:00", end_time: "2000-01-01 09:00:00", day_of_week: nil, created_at: "2013-07-23 14:29:06", updated_at: "2013-07-23 14:29:06">
Local time is ~09:30, UTC is ~14:30
Notice that the updated_at and created_at reflect the 14:30 UTC time but my start_time and end_time are not converted to UTC before being saved.
Ruby version: 1.9.3-p392
Rails version: 3.2.13

Resources