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?
Related
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".
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
I had an annoying problem with a freshly generated scaffold. Everything would run nicely, but while I could enter data, everytime I tried to save he would commit nil successfully. For all fields.
Looks like this:
Started POST "/spraches" for 127.0.0.1 at 2012-08-26 23:34:03 +0200
Processing by SprachesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nMf1+rBegI9BKoSAekzq8iLPQPAHpiiPk2DlmhEQxsQ=", "sprache"=>{"name"=>"23", "level"=>"23", "zertifikat"=>"124", "zertifikat_anders"=>"213", "zertifikat_note"=>"f23"}, "commit"=>"Create Sprache"}
[1m[36m (1.0ms)[0m [1mBEGIN[0m
[1m[35mSQL (29.0ms)[0m INSERT INTO "spraches" ("created_at", "level", "name", "updated_at", "zertifikat", "zertifikat_anders", "zertifikat_note") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["created_at", Sun, 26 Aug 2012 21:34:03 UTC +00:00], ["level", nil], ["name", nil], ["updated_at", Sun, 26 Aug 2012 21:34:03 UTC +00:00], ["zertifikat", nil], ["zertifikat_anders", nil], ["zertifikat_note", nil]]
[1m[36m (1.0ms)[0m [1mCOMMIT[0m
Redirected to http://localhost:3000/spraches/1
Completed 302 Found in 35ms (ActiveRecord: 31.0ms)
The reason become obivous to me after looking at the code in detail: I named my table in German (Sprache -> Language) and Rails tried to set it to plural. However, the controller only had it in the singular form.
#sprach = Sprache.new(params[:sprach])
I addded an "e" in the params to match the incoming code. Works.
The reason become obivous to me after looking at the code in detail: I named my table in German (Sprache -> Language) and Rails tried to set it to plural. However, the controller only had it in the singular form.
#sprach = Sprache.new(params[:sprach])
I addded an "e" in the params to match the incoming code. Works.
I am trying to generate some seed material.
seed_array.each do |seed|
Task.create(date: Date.new(2012,06,seed[1]), start_t: Time.new(2012,6,2,seed[2],seed[3]), end_t: Time.new(2012,6,2,seed[2] + 2,seed[3]), title: "#{seed[0]}")
end
Ultimately I will put random hours, minutes, seconds. The problem that I am facing is that instead of creating a time with the 2012-06-02 date it creates a time with a different date: 2000-01-01.
I tested Time.new(2012,6,2,2,20,45) in rails console and it works as expected. When I am trying to seed my database however some voodoo magic happens and I don't get the date I want.
Any inputs are appreciated. Thank you!
Update1:
(0.0ms) begin transaction SQL
(0.5ms) INSERT INTO "tasks" ("created_at", "date", "description",
"end_t", "group_id", "start_t", "title", "updated_at") VALUES (?, ?,
?, ?, ?, ?, ?, ?) [["created_at", Tue, 03 Jul 2012 02:15:34 UTC
+00:00], ["date", Thu, 07 Jun 2012], ["description", nil],
["end_t", 2012-06-02 10:02:00 -0400], ["group_id", nil],
["start_t", 2012-06-02 08:02:00 -0400], ["title", "99"],
["updated_at", Tue, 03 Jul 2012 02:15:34 UTC +00:00]]
(2.3ms) commit transaction
This is a small sample of the log.
Update 2
Task id: 101, date: "2012-06-26", start_t: "2000-01-01 08:45:00", end_t: "2000-01-01 10:45:00", title: "1", description: nil, group_id: nil, created_at: "2012-07-03 02:15:33", updated_at: "2012-07-03 02:15:33"
This is what shows up in rails console.
Sounds like you used the :time type for your start_t and end_t. In that context time refers to a pure time of day data type, with no date, because that's what the SQL time type is.
Ruby's Time type covers both date and time, so your database column needs to be :datetime. In fact ruby has no pure time of day class so activerecord uses a regular Time with a bogus date instead.
I have tested you code like this:
arr = [1,2,3]
arr.each do |seed|
Timetest.create(time: Time.new(2012,6,2,2,20,45), title: "#{seed}")
end
And its working as expected. But i got this sql on my console and its a bit different than yours in your start_t and end_t
SQL (4.1ms) INSERT INTO "timetests" ("created_at", "time", "title", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Tue, 03 Jul 2012 02:11:35 UTC +00:00], ["time", Sat, 02 Jun 2012 07:20:45 UTC +00:00], ["title", "1"], ["updated_at", Tue, 03 Jul 2012 02:11:35 UTC +00:00]]
Update 1
Have you tried to use DateTime.new instead of Time.new?
Task.create(date: DateTime.new(2012,06,seed[1]), start_t: DateTime.new(2012,6,2,seed[2],seed[3]), end_t: DateTime.new(2012,6,2,seed[2] + 2,seed[3]), title: "#{seed[0]}")
I have model Document:
class Document < ActiveRecord::Base
belongs_to :company
validates :name, :presence => true
end
And two classes inherited from Document:
License:
class License < Document
mount_uploader :file, DocumentUploader
end
And Certificate
class Certificate < Document
mount_uploader :file, DocumentUploader
end
And when I try to do current_company.licenses.create(...) or same action for the certificate, always all params are saved, besides file, which always is nil
Also I've tried to mount file inside of Document model... Help me please.
Here is logs:
Started POST "/companies/1/verified" for 127.0.0.1 at Mon Mar 19 09:33:41 +0200 2012
Processing by CompaniesController#verified as HTML
Parameters: {"verified"=>{"certificate"=>{"name"=>"Certificate", "file"=>"test.png"}, "insured"=>"2000000", "suppliers"=>"", "license"=>{"name"=>"License", "file"=>"test.png"}}, "authenticity_token"=>"0hIn41Tjonm/AXZBKM1PE/tjQxJDLqZaojMTHDoZq2k=", "id"=>"1", "utf8"=>"✓", "commit"=>"Update verifications"}
Company Load (0.7ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.8ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "License"], ["type", "License"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.6ms) COMMIT
(0.1ms) BEGIN
SQL (0.5ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "Certificate"], ["type", "Certificate"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.4ms) COMMIT
Redirected to http://localhost:3000/company/profile
Completed 302 Found in 18ms (ActiveRecord: 3.3ms)
Thanks.
Are you sure you've set the form that you've uploading the file with to have a multipart payload?
If not the file won't get sent on submission, and rails will only receive the textual form data. Inside the form_helper tag you'll need to add.
:html => {:multipart => true}