I have a User model with a draft_record boolean column which has a default of true. When creating records they are created with draft_record: false rather than true. This worked when the field was called draft however then the draft association and draft attribute assignment methods clashed resulting in the draft attribute being unsettable. Am I doing something wrong? I have had this problem before and just worked around it by reverting to what worked.
Ruby: 1.9.3-p327
Ruby on Rails: 3.2.12
DBMS: Postgres
Relevant migration:
class AddDraftColumnToUsers < ActiveRecord::Migration
def self.up
add_column :users, :draft_record, :boolean, default: true
add_column :users, :draft_id, :integer
add_column :users, :current_id, :integer
end
def self.down
...
end
end
Resultant schema
ActiveRecord::Schema.define(:version => 20130303002123) do
create_table "users", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "name"
t.boolean "draft_record", :default => true
t.integer "draft_id"
t.integer "current_id"
end
end
Creating a user object:
Loading development environment (Rails 3.2.12)
1.9.3-p327 :001 > u = User.create(name: "Jon")
(0.0ms) begin transaction
SQL (28.8ms) INSERT INTO "users" ("created_at", "current_id", "draft_id", "draft_record", "name", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00], ["current_id", nil], ["draft_id", nil], ["draft_record", false], ["name", "Jon"], ["updated_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00]]
(0.7ms) commit transaction
=> #<User id: 1, created_at: "2013-03-03 00:42:04", updated_at: "2013-03-03 00:42:04", name: "Jon", draft_record: false, draft_id: nil, current_id: nil>
1.9.3-p327 :002 >
This problem was caused by a default_scope with the conditions hash set to draft_record: false. This forced any record being added through active record to set draft_record to false.
The default value is set at DB level. So the insert query will not generate the default. Check the records inserted if they have correct default value set.
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'm learning ruby on rails and this is my first question in SO. I'm making a DB to manage products. In part of my squema I have users that can have many brands, and brands that can have many users. So my squema looks like this:
class Brand < ActiveRecord::Base
has_and_belongs_to_many :users
.
.
.
end
class User < ActiveRecord::Base
has_and_belongs_to_many :brands
.
.
.
end
ActiveRecord::Schema.define(version: 20160614163029) do
create_table "brands", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "brands_users", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "brand_id"
end
add_index "brands_users", ["brand_id"], name: "index_brands_users_on_brand_id"
add_index "brands_users", ["user_id"], name: "index_brands_users_on_user_id"
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
So when I create and user it has to put the name of his brand so I can create a brand associated to that user.
user.brands.create(name: "Boo")
This creates the association of the user with the brand so I can call user.brands and it gives me "Boo". My problem is that when I call:
brand.users
I get
#<ActiveRecord::Associations::CollectionProxy []>
I thought the association of this two objects should be automatic I have been searching but I don't know what I'm missing. Do I need to make manually the association from that brand to the user?
Thanks
Fixed. Reloading console fixed the issue. And now it works like I wanted:
2.3.1 :001 > user = User.second
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1
=> #<User id: 2, name: "Luis Hirthe DVM", email: "example-1#example.org", created_at: "2016-06-14 16:52:17", updated_at: "2016-06-14 16:52:17">
2.3.1 :002 > user.brands.create(name: "Foo")
(0.1ms) SAVEPOINT active_record_1
SQL (0.5ms) INSERT INTO "brands" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Foo"], ["created_at", "2016-06-14 20:23:08.410200"], ["updated_at", "2016-06-14 20:23:08.410200"]]
SQL (0.2ms) INSERT INTO "brands_users" ("user_id", "brand_id") VALUES (?, ?) [["user_id", 2], ["brand_id", 1]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<Brand id: 1, name: "Foo", created_at: "2016-06-14 20:23:08", updated_at: "2016-06-14 20:23:08">
2.3.1 :003 > brand = Brand.first
Brand Load (0.3ms) SELECT "brands".* FROM "brands" ORDER BY "brands"."id" ASC LIMIT 1
=> #<Brand id: 1, name: "Foo", created_at: "2016-06-14 20:23:08", updated_at: "2016-06-14 20:23:08">
2.3.1 :004 > brand.users.all
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "brands_users" ON "users"."id" = "brands_users"."user_id" WHERE "brands_users"."brand_id" = ? [["brand_id", 1]]
=> #<ActiveRecord::AssociationRelation [#<User id: 2, name: "Luis Hirthe DVM", email: "example-1#example.org", created_at: "2016-06-14 16:52:17", updated_at: "2016-06-14 16:52:17">
I hope this relationship example help other beginners.
I followed this tutorial to add a many to many association:
https://www.youtube.com/watch?v=ZJXHmesqJr0&index=35&list=PLvG9ngQqIEISob71DI7Id68lXLQ4hVoVH
My goal is to create a products table, a choices table and a connection between these tables (many to many). On those connections I need to store information like price.
This is my code
Schema.rb:
ActiveRecord::Schema.define(version: 20141213005617) do
create_table "choice_product_links", force: true do |t|
t.integer "price"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "choice_id"
t.integer "product_id"
end
create_table "choices", force: true do |t|
t.string "name"
t.decimal "price"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Models are:
class Choice < ActiveRecord::Base
has_many :choice_product_links
has_many :products, :through => :choice_product_links
end
class ChoiceProductLink < ActiveRecord::Base
belongs_to :product
belongs_to :choice
end
class Product < ActiveRecord::Base
has_many :choice_product_links
has_many :choices, :through => :choice_product_links
end
When I use the console to test my code, I enter a product p1 (works fine), a choice c1 (works fine). The problem occurs when I do the following:
>> cpl1 = ChoiceProductLink.new
#<ChoiceProductLink id: nil, price: nil, created_at: nil, updated_at: nil, choice_id: nil, product_id: nil>
>> cpl1.save
true
>> cpl1.product = p1
NoMethodError: undefined method `product=' for #<ChoiceProductLink:0x00000101f31bf8>
Can somebody help me?
Thanks guys
My previous answer was wrong -- sorry. I'm not entirely sure why your model isn't working properly. I just set up a test Rails application and ran all of the code you ran with the same migrations and class structure, and got these results:
Joshuas-MacBook-Pro:models joshua$ rails console
Loading development environment (Rails 4.0.0)
irb(main):001:0> cpl1 = ChoiceProductLink.new
=> #<ChoiceProductLink id: nil, price: nil, created_at: nil, updated_at: nil, choice_id: nil, product_id: nil>
irb(main):002:0> cpl1.save
(0.1ms) begin transaction
SQL (7.0ms) INSERT INTO "choice_product_links" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Sat, 13 Dec 2014 03:12:50 UTC +00:00], ["updated_at", Sat, 13 Dec 2014 03:12:50 UTC +00:00]]
(0.7ms) commit transaction
=> true
irb(main):004:0* p1 = Product.new
=> #<Product id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):005:0> p1.save
(0.1ms) begin transaction
SQL (1.5ms) INSERT INTO "products" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Sat, 13 Dec 2014 03:13:25 UTC +00:00], ["updated_at", Sat, 13 Dec 2014 03:13:25 UTC +00:00]]
(2.5ms) commit transaction
=> true
irb(main):006:0> cpl1.product = p1
=> #<Product id: 1, name: nil, created_at: "2014-12-13 03:13:25", updated_at: "2014-12-13 03:13:25">
irb(main):007:0> cpl1.save
(0.1ms) begin transaction
SQL (0.7ms) UPDATE "choice_product_links" SET "product_id" = ?, "updated_at" = ? WHERE "choice_product_links"."id" = 1 [["product_id", 1], ["updated_at", Sat, 13 Dec 2014 03:13:35 UTC +00:00]]
(1.6ms) commit transaction
=> true
Is this similar to what you did? As far as I can tell it works...
The solution is that I create a migration file that contains the following code:
class CreateConstraints2 < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.timestamps
end
create_table :choices do |t|
t.string :name
t.timestamps
end
create_table : choice_product_links do |t|
t.belongs_to :product
t.belongs_to :choice
t.datetime : choice_product_links
t.timestamps
end
end
end
With that migration file everything works perfectly. What I did before was that I created the tables with separate migration files and added the "has_many" and "belongs_to" later.
I dont know why thats different, but that's the solution. I tried almost 100 times back and forth :p
I have a fairly simple has many / belongs to relationship model: Reports have many Records have many Related Publishers. The belongs to objects have a foreign key to ID their parent.
However, I cannot save any 'Related Publishers'. Or rather I 'can', where the commit transaction returns true, but the Related Record is not created and the corresponding SQLite3 command does not make sense.
Why is the SQLite action generated by Rails the command to create an empty Record? Why is the save of the Related Publishers circumvented?
In rails console, with some output truncated
> #report = Report.new => #<Report id: nil, ... >
> #record = #report.records.build(leid: 1234567890) => #<Record id: nil, leid: 1234567890, ...>
> #related_publisher = #record.related_publishers.build(sid: 9876)
=> #<RelatedPublisher id: nil, ..., sid: 9876, ...>
> #report.save
(0.1ms) begin transaction
SQL (4.5ms) INSERT INTO "reports" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
SQL (0.2ms) INSERT INTO "records" ("created_at", "leid", "report_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["leid", 1234567890], ["report_id", 22], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
SQL (0.3ms) INSERT INTO "records" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Wed, 07 May 2014 11:20:50 UTC +00:00], ["updated_at", Wed, 07 May 2014 11:20:50 UTC +00:00]]
(0.7ms) commit transaction
=> true
> RelatedPublisher.all
RelatedPublisher Load (0.3ms) SELECT "related_publishers".* FROM "related_publishers"
=> #<ActiveRecord::Relation []>
(Using Rails 4.0.4 with sqlite3 3.7.13 with gem version 1.3.9 and Ruby 2.0.0p353 on Mac OSX 10.9.2
app/models/report.rb
class Report < ActiveRecord::Base
has_many :records
end
app/models/record.rb
class Record < ActiveRecord::Base
belongs_to :report
has_many :related_publishers
end
app/models/related_publisher.rb
class RelatedPublisher < ActiveRecord::Base
belongs_to :record
end
From schema.rb
create_table "records", force: true do |t|
t.integer "leid"
t.integer "sid"
t.string "name"
t.string "url"
t.string "join_date"
t.string "join_ip"
t.string "country"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "report_id"
t.boolean "ipAddressMatch"
t.boolean "whoisAddressMatch"
t.integer "recommendation"
end
add_index "records", ["report_id"], name: "index_records_on_report_id"
create_table "related_publishers", force: true do |t|
t.integer "leid"
t.integer "sid"
t.string "name"
t.string "url"
t.string "join_date"
t.string "join_ip"
t.string "country"
t.integer "record_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "related_publishers", ["record_id"], name: "index_related_publishers_on_record_id"
create_table "reports", force: true do |t|
t.integer "report_type"
t.string "upload_data_file_path"
t.string "completed_data_file_path"
t.datetime "created_at"
t.datetime "updated_at"
t.string "ltn_username"
end
I have not seen #new when building an association. Quoting from the Active Record Associations docs you will find
4.3.1.14 collection.build(attributes = {}, ...)
The collection.build method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will not yet be saved.
which seems to be what you want to achieve in your irb session. In your case this should translate into
#record.related_records.build(val: "related")
#record.save
(note that you save the parent, it will store its changed children too)
Alternatively you may use
4.3.1.15 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and, once it passes all of the validations specified on the associated model, the associated object will be saved.
if the newly created record should be saved right away (and the parent object is saved already).
I apologize for asking such a noob question, but I'm really confused why this isn't working. I'm creating a reddit clone as a way to teach myself rails. When a new Post is created, I would like to set the upvote and downvote attributes to zero. What am I doing wrong?
EDIT: It looks like before_save works when I create a new Post through my rails server, but it does not work when creating a new Post in the console. Why is this?
post.rb
class Post < ActiveRecord::Base
attr_accessible :title, :url, :upvotes, :downvotes
before_save :set_votes_to_zero
def set_votes_to_zero
self.upvotes = 0
self.downvotes = 0
end
end
schema.rb
ActiveRecord::Schema.define(:version => 20121223192629) do
create_table "posts", :force => true do |t|
t.string "title"
t.string "url"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "upvotes"
t.integer "downvotes"
end
end
Rails console output when creating a new post
irb(main):035:0> Post.create(title: "Facebook", url: "www.facebook.com")
←[1m←[35m (0.0ms)←[0m begin transaction
←[1m←[36mSQL (0.0ms)←[0m ←[1mINSERT INTO "posts" ("created_at", "downvotes",
"title", "updated_at", "upvotes", "url") VALUES (?, ?, ?, ?, ?, ?)←[0m [["creat
ed_at", Sun, 23 Dec 2012 19:43:37 UTC +00:00], ["downvotes", nil], ["title", "Fa
cebook"], ["updated_at", Sun, 23 Dec 2012 19:43:37 UTC +00:00], ["upvotes", nil]
, ["url", "www.facebook.com"]]
←[1m←[35m (124.8ms)←[0m commit transaction
=> #<Post id: 9, title: "Facebook", url: "www.facebook.com", created_at: "2012-1
2-23 19:43:37", updated_at: "2012-12-23 19:43:37", upvotes: nil, downvotes: nil>