I'm using Rails to make a scheduling app with Postgres. I have a model Shift with two timedate columns, start_at and end_at. The migration to create it follows:
class CreateShifts < ActiveRecord::Migration
def change
create_table :shifts do |t|
t.references :user
t.references :schedule
t.datetime :start_at
t.datetime :end_at
t.string :position
t.string :notes
t.timestamps
end
add_index :shifts, :user_id
add_index :shifts, :schedule_id
end
end
When I try to create a record I get the following error 500 output:
GError: ERROR: column "end_at" is of type timestamp without time zone but expression is of type time without time zone at character 132
HINT: You will need to rewrite or cast the expression.
: INSERT INTO "shifts" ("created_at", "end_at", "notes", "position", "schedule_id", "start_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"
I am creating a valid DateTime object before the DB save, because I've used the logger to display it and it is correct. The params look like this:
{"utf8"=>"✓",
"authenticity_token"=>"qornHMTAdikZJP/sByPIg//fFuYHHAQH3M/0R9XnP+o=",
"shift"=>{"user_id"=>"1",
"start_at(1i)"=>"2011",
"start_at(2i)"=>"11",
"start_at(3i)"=>"14",
"start_at(4i)"=>"00",
"start_at(5i)"=>"01",
"end_at(1i)"=>"2011",
"end_at(2i)"=>"11",
"end_at(3i)"=>"14",
"end_at(4i)"=>"00",
"end_at(5i)"=>"02",
"position"=>"",
"notes"=>""},
"schedule_id"=>"1"}
However, I can create a record via console by setting both fields to Time.now.
The error message is quite clear:
Your $2 in the INSERT statement is of type time instead of type timestamp (datetime)
INSERT INTO "shifts"
("created_at", "end_at", "notes", "position", "schedule_id", "start_at", "updated_at", "user_id")
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"
Emphasis mine.
You must be mixing up parameters, $2 is obviously not the one you display at the end of your posting. Or you inadvertently cast it to time before assigning. The part where you assign $2 is not visible in the question, that is where the problem occurs, most likely.
Maybe some kind of typo like in your question where you mention timedate instead of datetime?
Related
I've recently picked up maintenance of a couple of Rails 5.2 apps with a PostgreSQL back end. I'm new to Rails, but I've got a fair bit of experience on the various Microsoft platforms.
I'm trying to add API calls to an existing model. When I attempt to create a new instance, I am not getting the database-generated ID back:
POST /invoices
{ "amount": 12.34 }
Invoice Create (4.0ms)
INSERT INTO "invoices" ("amount", "created_at", "updated_at")
VALUES ($1, $2, $3)
[["amount", 12.34], ["created_at", "..."], ["updated_at", "..."]]
201 Created
{ "id": null, "amount": 12.34 }
Checking the database, the new row is present, with a unique ID.
A different model in the same app generates different SQL and works as expected:
POST /customer
{ "name": "ACME" }
Customer Create (1.4ms)
INSERT INTO "customers" ("name", "created_at", "updated_at")
VALUES ($1, $2, $3)
** RETURNING "id" **
[["name", "ACME"], ["created_at", "..."], ["updated_at", "..."]]
201 Created
{ "id": 111, "name": "ACME" }
I can't see any differences in the two models that explain this behavior. I've checked everything I can think of:
routes (via :resources)
controller
before/after filters
strong parameters
code in create
model
neither contains any code
schema
column definitions are comparable in schema.rb and information_schema.columns
Here's the model and controller for the misbehaving type:
class Invoice < ActiveRecord::Base
end
class InvoiceController < ApplicationController
def create
invoice = Invoice.new(invoice_params)
if invoice.save
# invoice.id.nil? => true
render json: invoice, status: :created
end
end
def invoice_params
params.permit(:amount)
end
end
# schema.rb
create_table "invoices", id: false, force: :cascade do |t|
t.serial "id", null: false
t.float "amount"
t.datetime "created_at"
t.datetime "updated_at"
end
And the one that works as expected:
class Customer < ActiveRecord::Base
end
class CustomerController < ApplicationController
def create
customer = Customer.new(customer_params)
if customer.save
# customer.id.nil? => false
render json: customer, status: :created
end
end
def customer_params
params.permit(:name)
end
end
# schema.rb
create_table "customers", id: :serial, force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
Replacing new/save with create or create! doesn't change the behavior, so I'm convinced that the problem is somewhere in the model definition or metadata.
Creating the models from rails console has the same result as shown below:
irb(main):001:0> Invoice.create(amount:12.34)
(0.8ms) BEGIN
Invoice Create (1.1ms) INSERT INTO "invoices" ("amount", "created_at", "updated_at") VALUES ($1, $2, $3) [["amount", 12.34], ["created_at", "2021-11-19 09:10:33.490117"], ["updated_at", "2021-11-19 09:10:33.490117"]]
(5.8ms) COMMIT
=> #<Invoice id: nil, amount: 12.34, created_at: "2021-11-19 09:10:33", updated_at: "2021-11-19 09:10:33">
irb(main):002:0> Customer.create(name: "ACME")
(0.9ms) BEGIN
Customer Create (1.5ms) INSERT INTO "customers" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "ACME"], ["created_at", "2021-11-19 09:12:50.492927"], ["updated_at", "2021-11-19 09:12:50.492927"]]
(13.3ms) COMMIT
=> #<Customer id: 24, name: "ACME", created_at: "2021-11-19 09:12:50", updated_at: "2021-11-19 09:12:50">
Can anyone point me in the right direction?
The difference is that you explicitly declared "id" as a column, and disabled the default primary key "id" declaration / handling.
If you change to:
create_table "invoices", id: :serial, force: :cascade do |t|
t.float "amount"
# These are normally created automatically so not sure why they are here
# t.datetime "created_at"
# t.datetime "updated_at"
end
it should work.
This answer may also help: https://stackoverflow.com/a/54694863/224837
This is caused by the missing primary key on the tables in question. It looks like these tables might have been created manually at some point early in the project, and have only been written to by external SQL scripts until now.
I am developing a simple app where a user can add a subject to a cart. But there is an error about not null constraint.
The error message browser shows is like that.
SQLite3::ConstraintException: NOT NULL constraint failed: items.title: INSERT INTO "items" ("image", "created_at", "updated_at") VALUES (?, ?, ?)
I've tried deleting not null constraint in schema.rb. But the error message is still on. So, what should I do?
Schema:
create_table "items", force: :cascade do |t|
t.string "image", null: false
t.string "title", null: false
t.string "description", null: false
t.string "stock", null: false
t.string "price", null: false
t.integer "status", limit: 1, default: 0, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Controller:
class SellController < ApplicationController
def new
#item = Item.new
end
def confirm
#item = Item.new(title: params[:title],price: params[:price],stock: params[:stock],description: params[:description],image: "default_item.jpg")
render :new if #item.invalid?
end
def create
#item = Item.new(title: params[:title],price: params[:price],stock: params[:stock],description: params[:description],image: "default_item.jpg")
##item = Item.new(item_params)
if params[:image]
#item.image = "#{#item.id}.jpg"
image = params[:image]
File.binwrite("public/item_images/#{#item.image}", image.read)
end
if params[:back]
format.html { render :new }
elsif #item.save
flash[:notice] = "your item data is saved."
redirect_to("/sell/complete")
else
render("sell/new")
end
end
def complete
end
end
I expect item data be saved and the page on browser is changed to thanks page.
Take a closer look at your error message:
SQLite3::ConstraintException: NOT NULL constraint failed: items.title: INSERT INTO "items" ("image", "created_at", "updated_at") VALUES (?, ?, ?)
It says that failed not null constraint on items.title column. And indeed, you don't provide title in your insert statement. It means that they were not passed to Item.new in your controller code.
I can see two solutions - if you want to keep all those constraints (you have multiple not-null columns in your table), you should also add active record presence validations. They will prevent calling insert statement to the database if values are missing and they will give you nice error messages in #item.error.
If you can allow those columns to be nullable, you can drop the constraint in a migration:
change_column_null :items, :title, false
You can also rollback the migration that created items table and edit this migration to avoid setting NOT NULL constraint there.
schema.rb is generated when you run migrations and can be used to load schema into a database. It's not done automatically though, you need to run a proper rake task (rake db:schema:load). It's not advisable to edit this file manually, as it's auto-generated and your changes will be automatically overwritten.
I have two models division definition and model definition
division definition :
has_many :model_definitions, inverse_of: :division_definition, foreign_key: :division_definition_id, primary_key: :division_definition_id
ModelDefinition:
belongs_to :division_definition, inverse_of: :model_definitions, foreign_key: :division_id, primary_key: :division_id
In Db structure
def change
create_table :model_definitions do |t|
t.string :model_id , null: false, unique: true
t.string :car_model_name , null: false
t.belongs_to :division_definition , null: false
t.timestamps null: false
end
add_index :model_definitions, :division_definition_id
add_foreign_key :model_definitions, :division_definitions, on_delete: :restrict
end
divisiondefinition table
def change
create_table :division_definitions do |t|
t.integer :division_definition_id, null: false
t.string :division_name,null: false
t.timestamps null: false
end
add_index :division_definitions, :division_definition_id, unique: true
end
When i try to create model definition through division definition in console i'm getting following error
PG::ForeignKeyViolation: ERROR: insert or update on table "model_definitions" violates foreign key constraint "fk_rails_addc8b742e"
DETAIL: Key (division_definition_id)=(124) is not present in table "division_definitions".
: INSERT INTO "model_definitions" ("model_id", "car_model_name", "division_definition_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
(0.3ms) ROLLBACK
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "model_definitions" violates foreign key constraint "fk_rails_addc8b742e"
DETAIL: Key (division_definition_id)=(124) is not present in table "division_definitions".
: INSERT INTO "model_definitions" ("model_id", "car_model_name", "division_definition_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
from /home/love/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
This is what i tried,
DivisionDefinition.create(division_definition_id: '124', division_name: 'BMW')
a = DivisionDefinition.last.model_definitions.build(model_id: 'bmw1', car_model_name: 'bmw1')
a = DivisionDefinition.last.model_definitions.build(model_id: 'bmw1', car_model_name: 'bmw1')
a.save
If i remove add_foreign_key from db i am able to create model definition through divisiondefinition
what is the error i'm making in migration file?
Your ModelDefinitions class refers to :division_id which appears nowhere else in your code.
I am "banging" my head against a wall trying to figure this out. I decided to allow users to log in through twitter, Google,my site or Facebook. The problem is that twitter does not provide emails, so I am trying to add users by username. The problem with that is devise keeps checking for email and when I don't require it, I get PG::Error: ERROR: duplicate key value violates unique constraint "index_users_on_email" DETAIL: Key (email)=() already exists. : INSERT INTO "users" ("created_at", "name", "provider", "uid", "updated_at", "username") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id".
I don't know what I should do. I created a username column in my database, and I made sure to add this to my devise.rb intializer, config.authentication_keys = [ :username ]
I think that's because default Devise installation creates unique key on email coulmn. In your case it's empty, hence second user doesn't have unique one (because empty string is taken by the first user).
Migration in my case
class AllowNullEmail < ActiveRecord::Migration
def up
remove_index :users, :email
change_column :users, :email, :string, :null => true
end
def down
change_column :users, :email, :string, :null => false
add_index :users, :email, unique: true
end
end
I just recently made the switch over to PostgreSQL.. and just wondering about this issue:
PG::Error: ERROR: column "bathrooms" is of type double precision but expression is of type character varying at character 161
HINT: You will need to rewrite or cast the expression.
: INSERT INTO "properties" ("address", "bathrooms", "bedrooms", "city", "country", "created_at", "description", "name", "state", "updated_at", "zip") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id"
Migration looks like this:
class CreateProperties < ActiveRecord::Migration
def change
create_table :properties do |t|
t.string :name
t.string :address
t.string :city
t.string :state
t.string :zip
t.string :country
t.float :bedrooms
t.float :bathrooms
t.string :country
t.text :description
t.timestamps
end
end
end
I switched the float to decimal, it seems PostgreSQL is fine with that implicit cast now?
Either way the error is gone, thanks