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".
Related
I want to save Json data in my Rails PostgreSQL database.
In my migration file
class IbmSubscription < ActiveRecord::Migration
def up
create_table :ibm_subscriptions do |t|
t.json 'ibm_response'
t.references :user, index: true
t.timestamps
end
end
def down
drop_table :ibm_subscriptions
end
end
I am unable to save JSON data in ibm_response
I tried this in rails console
2.1.5 :004 > a = JSON.parse(#uri.to_s)
=> {"type"=>"SUBSCRIPTION_ORDER", "marketplace"=>{"base_url"=>"https://acme.appdirect.com", "partner"=>"ACME"}, "flag"=>"STATELESS", "creator"=>{"email"=>"test-email+creator#appdirect.com", "first_name"=>"DummyCreatorFirst", "language"=>"fr", "last_name"=>"DummyCreatorLast", "open_id"=>"https://www.appdirect.com/openid/id/ec5d8eda-5cec-444d-9e30-125b6e4b67e2", "uuid"=>"ec5d8eda-5cec-444d-9e30-125b6e4b67e2"}, "payload"=>{"company"=>{"country"=>"CA", "email"=>"company-email#example.com", "name"=>"Example Company Name", "phone_number"=>"415-555-1212", "uuid"=>"d15bb36e-5fb5-11e0-8c3c-00262d2cda03", "website"=>"http://www.example.com"}, "configuration"=>{"entry"=>{"key"=>"domain", "value"=>"mydomain"}}, "order"=>{"edition_code"=>"BASIC", "pricing_duration"=>"MONTHLY", "item"=>[{"quantity"=>"10", "unit"=>"USER"}, {"quantity"=>"15", "unit"=>"MEGABYTE"}]}}, "return_url"=>"https://www.appdirect.com/finishprocure?token=dummyOrder", "#xmlns:atom"=>"http://www.w3.org/2005/Atom"}
2.1.5 :005 > a = IbmSubscription.create(ibm_response: #uri)
WARNING: Can't mass-assign protected attributes for IbmSubscription: ibm_response
(0.2ms) BEGIN
SQL (5.7ms) INSERT INTO "ibm_subscriptions" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Fri, 02 Oct 2015 17:30:13 UTC +00:00], ["updated_at", Fri, 02 Oct 2015 17:30:13 UTC +00:00]]
(93.9ms) COMMIT
=> #<IbmSubscription id: 1, ibm_response: nil, user_id: nil, created_at: "2015-10-02 17:30:13", updated_at: "2015-10-02 17:30:13">
Its creating ibm_response: nil.
Please help me. Thanks
Can't mass-assign protected attributes for IbmSubscription
This line implies that you need to add :ibm_response to attr_accessible in your IbmSubscription model.
In my rails 4.2 app I have set it as the default locale by setting it in config/application.rb:
config.i18n.default_locale = :it
I have a simple "product" model
class Product < ActiveRecord::Base
translates :name, :description
end
My need is when I18n.locale = :it the content should be written in "products" table while for all others locales the content shoud go in the "product_translations" table.
Currently what happens is the following:
if
config.i18n.default_locale = :en
content is written to the "products" table, for all different locales the content goes to the "product_translations" table.
How can I change this?
EDIT
Using the console to test globalize behaviour I found that maybe I did not understand how globalize should work.
I was expecting that the "products" table is filled with default_locale (in my case :it) and the "product_translations" table is filled with other locales (:en, :fr, :de and so on).
Instead I see that whichever the locale is, fields that are indicated as
translates :name, :description
are always written in "product_translations" and "product" table only contains those fields that are not translated (in my case uom (unit of measure).
This is the output of the console after saving a new product with :en as locale.
[18] pry(main)> en_p=Product.create(:name=>"butter",
:description => "82% min fat butter",
:uom => "kg") (0.3ms)
BEGIN
SQL (0.7ms)
INSERT INTO "products" ("uom", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
[
["uom", "kg"],
["created_at", "2015-07-14 05:49:09.097092"],
["updated_at", "2015-07-14 05:49:09.097092"]
]
SQL (0.7ms)
INSERT INTO "product_translations" ("locale",
"name",
"description",
"product_id",
"created_at",
"updated_at")
VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
[
["locale", "en"],
["name", "butter"],
["description", "82% min fat butter"],
["product_id", 5],
["created_at", "2015-07-14 05:49:09.116683"],
["updated_at", "2015-07-14 05:49:09.116683"]
]
(15.9ms)
COMMIT
=> #<Product:0xb63d3568
id: 5,
name: "butter",
description: "82% min fat butter",
uom: "kg",
created_at: Tue, 14 Jul 2015 05:49:09 UTC +00:00,
updated_at: Tue, 14 Jul 2015 05:49:09 UTC +00:00>
[19] pry(main)> I18n.locale=:it
=> :it
[20] pry(main)> it_p=Product.create(:name=>"olio di oliva EVO",
:description => "Olio di oliva extravergine spremuto a freddo",
:uom => "kg") (0.4ms)
BEGIN
SQL (0.5ms)
INSERT INTO "products" ("uom", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
[
["uom", "kg"],
["created_at", "2015-07-14 05:51:34.772755"],
["updated_at", "2015-07-14 05:51:34.772755"]
]
SQL (0.8ms)
INSERT INTO "product_translations" ("locale",
"name",
"description",
"product_id",
"created_at",
"updated_at")
VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
[
["locale", "it"],
["name", "olio di oliva extravergine"],
["description", "Olio di oliva extravergine ottenuto unicamente per spremitura"],
["product_id", 6],
["created_at", "2015-07-14 05:51:34.779220"],
["updated_at", "2015-07-14 05:51:34.779220"]
]
(16.1ms)
COMMIT
=> #<Product:0xb6315ce8
id: 6,
name: "olio di oliva extravergine",
description: "Olio di oliva extravergine ottenuto unicamente per spremitura",
uom: "kg",
created_at: Tue, 14 Jul 2015 05:51:34 UTC +00:00,
updated_at: Tue, 14 Jul 2015 05:51:34 UTC +00:00>
Is this the default behaviour?
Do I need to remove translatable fields from the original table?
folks.
When issuing the following code block in the controller, the resulting SQL does not include one column/attr of the model:
dev_total = DeviceTotal.where(time_stamp: #time_stamp).first_or_create {|dt|
dt.dev_utc = #utc,
dt.value = #grand_total,
dt.production = #net_weight,
dt.device_id = #device.id,
dt.var = #device.variable.symbol
}
Resulting SQL:
INSERT INTO "device_totals" ("created_at", "device_id", "production", "time_stamp",
"updated_at", "value", "var") VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING "id" [["created_at", Wed, 09 Oct 2013 22:54:04 UTC +00:00],
["device_id", 1], ["production", 80.38], ["time_stamp", Wed, 09 Oct 2013 22:53:59
UTC +00:00], ["updated_at", Wed, 09 Oct 2013 22:54:04 UTC +00:00], ["value",
847.25], ["var", "FRL"]]
As one can see, the column 'dev_utc' is not included in the INSERT command. All other attributes are saved to the DB. This is a BigInt type column.
Everything else works fine.
Checked my code dozens of times w/o any luck.
Any clues?
Model
class Pm < ActiveRecord::Base
attr_accessor :name
end
Console
me = Pm.new
#=> <Pm id: nil, name: nil, created_at: nil, updated_at: nil>
me.name = "Josh"
#=> "Josh"
me.save
#=>(0.4ms) BEGIN
#=> true
#=> SQL (0.8ms) INSERT INTO "pms" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Sat, 01 Jun 2013 19:02:27 UTC +00:00], ["name", nil], ["updated_at", Sat, 01 Jun 2013 19:02:27 UTC +00:00]]
#=>(1.3ms) COMMIT
me
#=> <Pm id: 4, name: nil, created_at: "2013-06-01 19:02:27", updated_at: "2013-06-01 19:02:27">
I have a model with a name attribute and an attr_accessor defined. The record saves but it doesn't update the name attribute. Am I missing something simple here?
If your Pmp model ("Pimp"? "Pump"? "Pimple"?) has a DB field called "name", there's no reason to use attr_accessor :name. With attr_accessor :name, ActiveRecord's dynamically generated attribute methods will never be invoked, and yes, it means the attribute won't be saved to the database.
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